diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 12:06:34 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 12:06:34 +0000 |
commit | 5e61585d76ae77fd5e9e96ebabb57afa4d74880d (patch) | |
tree | 2b467823aaeebc7ef8bc9e3cabe8074eaef1666d /src/cleanup | |
parent | Initial commit. (diff) | |
download | postfix-upstream.tar.xz postfix-upstream.zip |
Adding upstream version 3.5.24.upstream/3.5.24upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cleanup')
190 files changed, 13157 insertions, 0 deletions
diff --git a/src/cleanup/.indent.pro b/src/cleanup/.indent.pro new file mode 120000 index 0000000..5c837ec --- /dev/null +++ b/src/cleanup/.indent.pro @@ -0,0 +1 @@ +../../.indent.pro
\ No newline at end of file diff --git a/src/cleanup/.printfck b/src/cleanup/.printfck new file mode 100644 index 0000000..66016ed --- /dev/null +++ b/src/cleanup/.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/cleanup/Makefile.in b/src/cleanup/Makefile.in new file mode 100644 index 0000000..546aae1 --- /dev/null +++ b/src/cleanup/Makefile.in @@ -0,0 +1,1320 @@ +SHELL = /bin/sh +SRCS = cleanup.c cleanup_out.c cleanup_envelope.c cleanup_message.c \ + cleanup_extracted.c cleanup_state.c cleanup_rewrite.c \ + cleanup_map11.c cleanup_map1n.c cleanup_masquerade.c \ + cleanup_out_recipient.c cleanup_init.c cleanup_api.c \ + cleanup_addr.c cleanup_bounce.c cleanup_milter.c \ + cleanup_body_edit.c cleanup_region.c cleanup_final.c +OBJS = cleanup.o cleanup_out.o cleanup_envelope.o cleanup_message.o \ + cleanup_extracted.o cleanup_state.o cleanup_rewrite.o \ + cleanup_map11.o cleanup_map1n.o cleanup_masquerade.o \ + cleanup_out_recipient.o cleanup_init.o cleanup_api.o \ + cleanup_addr.o cleanup_bounce.o cleanup_milter.o \ + cleanup_body_edit.o cleanup_region.o cleanup_final.o +HDRS = +TESTSRC = +DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) +CFLAGS = $(DEBUG) $(OPT) $(DEFS) +TESTPROG= cleanup_masquerade cleanup_milter +PROG = cleanup +INC_DIR = ../../include +LIBS = ../../lib/lib$(LIB_PREFIX)master$(LIB_SUFFIX) \ + ../../lib/libmilter.a \ + ../../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) + +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 + +cleanup_masquerade: cleanup_masquerade.o + mv cleanup_masquerade.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIBS) $(SYSLIBS) + mv junk cleanup_masquerade.o + +CLEANUP_MILTER_OBJS = cleanup_state.o cleanup_out.o cleanup_addr.o \ + cleanup_out_recipient.o cleanup_body_edit.o cleanup_region.o +cleanup_milter: cleanup_milter.o $(CLEANUP_MILTER_OBJS) $(LIBS) + mv cleanup_milter.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(CLEANUP_MILTER_OBJS) $(LIBS) $(SYSLIBS) + mv junk cleanup_milter.o + +tests: cleanup_masquerade_test milter_tests + +milter_tests: cleanup_milter_test bug_tests \ + cleanup_milter_test2 cleanup_milter_test3 cleanup_milter_test4 \ + cleanup_milter_test5 cleanup_milter_test6 cleanup_milter_test7 \ + cleanup_milter_test8 cleanup_milter_test9 cleanup_milter_test10a \ + cleanup_milter_test10b cleanup_milter_test10c cleanup_milter_test10d \ + cleanup_milter_test10e cleanup_milter_test11 cleanup_milter_test12 \ + cleanup_milter_test13a cleanup_milter_test13b cleanup_milter_test13c \ + cleanup_milter_test13d cleanup_milter_test13e cleanup_milter_test13f \ + cleanup_milter_test13g cleanup_milter_test13h cleanup_milter_test13i \ + cleanup_milter_test14a cleanup_milter_test14b cleanup_milter_test14c \ + cleanup_milter_test14d cleanup_milter_test14e cleanup_milter_test14f \ + cleanup_milter_test14g \ + cleanup_milter_test15a cleanup_milter_test15b cleanup_milter_test15c \ + cleanup_milter_test15d cleanup_milter_test15e cleanup_milter_test15f \ + cleanup_milter_test15g cleanup_milter_test15h cleanup_milter_test15i \ + cleanup_milter_test16a cleanup_milter_test16b + +root_tests: + +cleanup_masquerade_test: cleanup_masquerade cleanup_masq.ref + rm -f cleanup_masq.tmp + $(SHLIB_ENV) ./cleanup_masquerade '' a.b.c,b.c xxx@aa.a.b.c >>cleanup_masq.tmp + $(SHLIB_ENV) ./cleanup_masquerade '' A.B.C,B.C xxx@aa.a.b.c >>cleanup_masq.tmp + $(SHLIB_ENV) ./cleanup_masquerade '' a.b.c,b.c xxx@AA.A.B.C >>cleanup_masq.tmp + $(SHLIB_ENV) ./cleanup_masquerade 'xxx' a.b.c,b.c xxx@aa.a.b.c >>cleanup_masq.tmp + $(SHLIB_ENV) ./cleanup_masquerade 'yyy' a.b.c,b.c xxx@aa.a.b.c >>cleanup_masq.tmp + $(SHLIB_ENV) ./cleanup_masquerade '' !a.b.c,b.c xxx@aa.a.b.c >>cleanup_masq.tmp + $(SHLIB_ENV) ./cleanup_masquerade '' a.b.c,b.c xxx@a.b.c >>cleanup_masq.tmp + $(SHLIB_ENV) ./cleanup_masquerade '' !a.b.c,b.c xxx@a.b.c >>cleanup_masq.tmp + $(SHLIB_ENV) ./cleanup_masquerade '' a.b.c,b.c xxx@aaa.b.c >>cleanup_masq.tmp + $(SHLIB_ENV) ./cleanup_masquerade '' a.b.c,b.c xxx@b.c >>cleanup_masq.tmp + $(SHLIB_ENV) ./cleanup_masquerade 'fail:whatever' xy xxx@b.c >>cleanup_masq.tmp + diff cleanup_masq.ref cleanup_masq.tmp + rm -f cleanup_masq.tmp + +bug_tests: bug1_test bug2_test bug3_test + +../postcat/postcat: + cd ../postcat; make + +bug1_test: cleanup_milter bug1.file bug1.in bug1.ref bug1.text.ref \ + ../postcat/postcat + cp bug1.file bug1.file.tmp + chmod u+w bug1.file.tmp + $(SHLIB_ENV) ./cleanup_milter <bug1.in + $(SHLIB_ENV) ../postcat/postcat -ov bug1.file.tmp 2>/dev/null >bug1.tmp + diff bug1.ref bug1.tmp + $(SHLIB_ENV) ../postcat/postcat bug1.file.tmp 2>/dev/null >bug1.tmp + diff bug1.text.ref bug1.tmp + rm -f bug1.file.tmp bug1.tmp + +bug2_test: cleanup_milter bug2.file bug2.in bug2.ref bug2.text.ref \ + ../postcat/postcat + cp bug2.file bug2.file.tmp + chmod u+w bug2.file.tmp + $(SHLIB_ENV) ./cleanup_milter <bug2.in + $(SHLIB_ENV) ../postcat/postcat -ov bug2.file.tmp 2>/dev/null >bug2.tmp + diff bug2.ref bug2.tmp + $(SHLIB_ENV) ../postcat/postcat bug2.file.tmp 2>/dev/null >bug2.tmp + diff bug2.text.ref bug2.tmp + rm -f bug2.file.tmp bug2.tmp + +bug3_test: cleanup_milter bug3.file bug3.in bug3.ref bug3.text.ref \ + ../postcat/postcat + cp bug3.file bug3.file.tmp + chmod u+w bug3.file.tmp + $(SHLIB_ENV) ./cleanup_milter <bug3.in + $(SHLIB_ENV) ../postcat/postcat -ov bug3.file.tmp 2>/dev/null >bug3.tmp + diff bug3.ref bug3.tmp + $(SHLIB_ENV) ../postcat/postcat bug3.file.tmp 2>/dev/null >bug3.tmp + diff bug3.text.ref bug3.tmp + rm -f bug3.file.tmp bug3.tmp + +# Test queue file editing routines. + +cleanup_milter_test: cleanup_milter test-queue-file cleanup_milter.in1 \ + cleanup_milter.ref1 ../postcat/postcat + cp test-queue-file test-queue-file.tmp + chmod u+w test-queue-file.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in1 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref1 cleanup_milter.tmp + rm -f test-queue-file.tmp cleanup_milter.tmp + +cleanup_milter_test2: cleanup_milter test-queue-file2 cleanup_milter.in2 \ + cleanup_milter.ref2 ../postcat/postcat + cp test-queue-file2 test-queue-file2.tmp + chmod u+w test-queue-file2.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in2 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file2.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref2 cleanup_milter.tmp + rm -f test-queue-file2.tmp cleanup_milter.tmp + +cleanup_milter_test3: cleanup_milter test-queue-file3 cleanup_milter.in3 \ + cleanup_milter.ref3 ../postcat/postcat + cp test-queue-file3 test-queue-file3.tmp + chmod u+w test-queue-file3.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in3 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file3.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref3 cleanup_milter.tmp + rm -f test-queue-file3.tmp cleanup_milter.tmp + +cleanup_milter_test4: cleanup_milter test-queue-file4 cleanup_milter.in4a \ + cleanup_milter.in4b cleanup_milter.in4c cleanup_milter.ref4 \ + test-queue-file4 ../postcat/postcat + cp test-queue-file4 test-queue-file4.tmp + chmod u+w test-queue-file4.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in4a + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file4.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref4 cleanup_milter.tmp + cp test-queue-file4 test-queue-file4.tmp + chmod u+w test-queue-file4.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in4b + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file4.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref4 cleanup_milter.tmp + cp test-queue-file4 test-queue-file4.tmp + chmod u+w test-queue-file4.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in4c + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file4.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref4 cleanup_milter.tmp + rm -f test-queue-file4.tmp cleanup_milter.tmp + +cleanup_milter_test5: cleanup_milter test-queue-file5 cleanup_milter.in5 \ + cleanup_milter.ref5 ../postcat/postcat + cp test-queue-file5 test-queue-file5.tmp + chmod u+w test-queue-file5.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in5 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file5.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref5 cleanup_milter.tmp + rm -f test-queue-file5.tmp cleanup_milter.tmp + +cleanup_milter_test6: cleanup_milter_test6a cleanup_milter_test6b cleanup_milter_test6c + rm -f test-queue-file6.tmp cleanup_milter.tmp + +cleanup_milter_test6a: cleanup_milter test-queue-file6 cleanup_milter.in6a \ + cleanup_milter.ref6a test-queue-file6 ../postcat/postcat + cp test-queue-file6 test-queue-file6.tmp + chmod u+w test-queue-file6.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in6a + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file6.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref6a cleanup_milter.tmp + rm -f test-queue-file6.tmp cleanup_milter.tmp + +cleanup_milter_test6b: cleanup_milter test-queue-file6 cleanup_milter.in6b \ + cleanup_milter.ref6b ../postcat/postcat + cp test-queue-file6 test-queue-file6.tmp + chmod u+w test-queue-file6.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in6b + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file6.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref6b cleanup_milter.tmp + rm -f test-queue-file6.tmp cleanup_milter.tmp + +cleanup_milter_test6c: cleanup_milter test-queue-file6 cleanup_milter.in6c \ + cleanup_milter.ref6c ../postcat/postcat + cp test-queue-file6 test-queue-file6.tmp + chmod u+w test-queue-file6.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in6c + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file6.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref6c cleanup_milter.tmp + rm -f test-queue-file6.tmp cleanup_milter.tmp + +cleanup_milter_test7: cleanup_milter test-queue-file7 cleanup_milter.in7 \ + cleanup_milter.ref7 ../postcat/postcat + cp test-queue-file7 test-queue-file7.tmp + chmod u+w test-queue-file7.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in7 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file7.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref7 cleanup_milter.tmp + rm -f test-queue-file7.tmp cleanup_milter.tmp + +cleanup_milter_test8: cleanup_milter test-queue-file8 cleanup_milter.in8 \ + cleanup_milter.ref8 ../postcat/postcat + cp test-queue-file8 test-queue-file8.tmp + chmod u+w test-queue-file8.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in8 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file8.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref8 cleanup_milter.tmp + rm -f test-queue-file8.tmp cleanup_milter.tmp + +cleanup_milter_test9: cleanup_milter test-queue-file9 cleanup_milter.in9 \ + cleanup_milter.ref9 ../postcat/postcat + cp test-queue-file9 test-queue-file9.tmp + chmod u+w test-queue-file9.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in9 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file9.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref9 cleanup_milter.tmp + rm -f test-queue-file9.tmp cleanup_milter.tmp + +cleanup_milter_test10a: cleanup_milter test-queue-file10 cleanup_milter.in10a \ + cleanup_milter.ref10a ../postcat/postcat + cp test-queue-file10 test-queue-file10.tmp + chmod u+w test-queue-file10.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in10a + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file10.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref10a cleanup_milter.tmp + rm -f test-queue-file10.tmp cleanup_milter.tmp + +cleanup_milter_test10b: cleanup_milter test-queue-file10 cleanup_milter.in10b \ + cleanup_milter.ref10b ../postcat/postcat + cp test-queue-file10 test-queue-file10.tmp + chmod u+w test-queue-file10.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in10b + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file10.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref10b cleanup_milter.tmp + rm -f test-queue-file10.tmp cleanup_milter.tmp + +cleanup_milter_test10c: cleanup_milter test-queue-file10 cleanup_milter.in10c \ + cleanup_milter.ref10c ../postcat/postcat + cp test-queue-file10 test-queue-file10.tmp + chmod u+w test-queue-file10.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in10c + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file10.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref10c cleanup_milter.tmp + rm -f test-queue-file10.tmp cleanup_milter.tmp + +cleanup_milter_test10d: cleanup_milter test-queue-file10 cleanup_milter.in10c \ + cleanup_milter.ref10d ../postcat/postcat + cp test-queue-file10 test-queue-file10.tmp + chmod u+w test-queue-file10.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in10d + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file10.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref10d cleanup_milter.tmp + rm -f test-queue-file10.tmp cleanup_milter.tmp + +cleanup_milter_test10e: cleanup_milter test-queue-file10 cleanup_milter.in10c \ + cleanup_milter.ref10e ../postcat/postcat + cp test-queue-file10 test-queue-file10.tmp + chmod u+w test-queue-file10.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in10e + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file10.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref10e cleanup_milter.tmp + rm -f test-queue-file10.tmp cleanup_milter.tmp + +cleanup_milter_test11: cleanup_milter test-queue-file11 cleanup_milter.in11 \ + cleanup_milter.ref11 ../postcat/postcat + cp test-queue-file11 test-queue-file11.tmp + chmod u+w test-queue-file11.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in11 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file11.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref11 cleanup_milter.tmp + rm -f test-queue-file11.tmp cleanup_milter.tmp + +cleanup_milter_test12: cleanup_milter test-queue-file12 cleanup_milter.in12 \ + cleanup_milter.ref12 ../postcat/postcat + cp test-queue-file12 test-queue-file12.tmp + chmod u+w test-queue-file12.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in12 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file12.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref12 cleanup_milter.tmp + rm -f test-queue-file12.tmp cleanup_milter.tmp + +cleanup_milter_test13a: cleanup_milter test-queue-file13a cleanup_milter.in13a \ + cleanup_milter.ref13a ../postcat/postcat + cp test-queue-file13a test-queue-file13a.tmp + chmod u+w test-queue-file13a.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in13a + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file13a.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref13a cleanup_milter.tmp + rm -f test-queue-file13a.tmp cleanup_milter.tmp + +cleanup_milter_test13b: cleanup_milter test-queue-file13b cleanup_milter.in13b \ + cleanup_milter.ref13b ../postcat/postcat + cp test-queue-file13b test-queue-file13b.tmp + chmod u+w test-queue-file13b.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in13b + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file13b.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref13b cleanup_milter.tmp + rm -f test-queue-file13b.tmp cleanup_milter.tmp + +cleanup_milter_test13c: cleanup_milter test-queue-file13c cleanup_milter.in13c \ + cleanup_milter.ref13c ../postcat/postcat + cp test-queue-file13c test-queue-file13c.tmp + chmod u+w test-queue-file13c.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in13c + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file13c.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref13c cleanup_milter.tmp + rm -f test-queue-file13c.tmp cleanup_milter.tmp + +cleanup_milter_test13d: cleanup_milter test-queue-file13d cleanup_milter.in13d \ + cleanup_milter.ref13d ../postcat/postcat + cp test-queue-file13d test-queue-file13d.tmp + chmod u+w test-queue-file13d.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in13d + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file13d.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref13d cleanup_milter.tmp + rm -f test-queue-file13d.tmp cleanup_milter.tmp + +cleanup_milter_test13e: cleanup_milter test-queue-file13e cleanup_milter.in13e \ + cleanup_milter.ref13e ../postcat/postcat + cp test-queue-file13e test-queue-file13e.tmp + chmod u+w test-queue-file13e.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in13e + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file13e.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref13e cleanup_milter.tmp + rm -f test-queue-file13e.tmp cleanup_milter.tmp + +cleanup_milter_test13g: cleanup_milter test-queue-file13g cleanup_milter.in13g \ + cleanup_milter.ref13g ../postcat/postcat + cp test-queue-file13g test-queue-file13g.tmp + chmod u+w test-queue-file13g.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in13g + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file13g.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref13g cleanup_milter.tmp + rm -f test-queue-file13g.tmp cleanup_milter.tmp + +cleanup_milter_test13h: cleanup_milter test-queue-file13h cleanup_milter.in13h \ + cleanup_milter.ref13h ../postcat/postcat + cp test-queue-file13h test-queue-file13h.tmp + chmod u+w test-queue-file13h.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in13h + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file13h.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref13h cleanup_milter.tmp + rm -f test-queue-file13h.tmp cleanup_milter.tmp + +cleanup_milter_test13i: cleanup_milter test-queue-file13i cleanup_milter.in13i \ + cleanup_milter.ref13i ../postcat/postcat + cp test-queue-file13i test-queue-file13i.tmp + chmod u+w test-queue-file13i.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in13i + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file13i.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref13i cleanup_milter.tmp + rm -f test-queue-file13i.tmp cleanup_milter.tmp + +cleanup_milter_test13f: cleanup_milter test-queue-file13f cleanup_milter.in13f \ + cleanup_milter.ref13f ../postcat/postcat + cp test-queue-file13f test-queue-file13f.tmp + chmod u+w test-queue-file13f.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in13f + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file13f.tmp 2>/dev/null >cleanup_milter.tmp + diff cleanup_milter.ref13f cleanup_milter.tmp + rm -f test-queue-file13f.tmp cleanup_milter.tmp + +cleanup_milter_test14a: cleanup_milter test-queue-file14 cleanup_milter.in14a \ + cleanup_milter.ref14a1 ../postcat/postcat cleanup_milter.ref14a2 \ + cleanup_milter.reg14a + cp test-queue-file14 test-queue-file14a.tmp + chmod u+w test-queue-file14a.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in14a 2>cleanup_milter.tmp1 + diff cleanup_milter.ref14a1 cleanup_milter.tmp1 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file14a.tmp 2>/dev/null >cleanup_milter.tmp2 + diff cleanup_milter.ref14a2 cleanup_milter.tmp2 + rm -f test-queue-file14a.tmp cleanup_milter.tmp1 cleanup_milter.tmp2 + +cleanup_milter_test14b: cleanup_milter test-queue-file14 cleanup_milter.in14b \ + cleanup_milter.ref14b1 ../postcat/postcat cleanup_milter.ref14b2 \ + cleanup_milter.reg14b + cp test-queue-file14 test-queue-file14b.tmp + chmod u+w test-queue-file14b.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in14b 2>cleanup_milter.tmp1 + diff cleanup_milter.ref14b1 cleanup_milter.tmp1 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file14b.tmp 2>/dev/null >cleanup_milter.tmp2 + diff cleanup_milter.ref14b2 cleanup_milter.tmp2 + rm -f test-queue-file14b.tmp cleanup_milter.tmp1 cleanup_milter.tmp2 + +cleanup_milter_test14c: cleanup_milter test-queue-file14 cleanup_milter.in14c \ + cleanup_milter.ref14c1 ../postcat/postcat cleanup_milter.ref14c2 \ + cleanup_milter.reg14c + cp test-queue-file14 test-queue-file14c.tmp + chmod u+w test-queue-file14c.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in14c 2>cleanup_milter.tmp1 + diff cleanup_milter.ref14c1 cleanup_milter.tmp1 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file14c.tmp 2>/dev/null >cleanup_milter.tmp2 + diff cleanup_milter.ref14c2 cleanup_milter.tmp2 + rm -f test-queue-file14c.tmp cleanup_milter.tmp1 cleanup_milter.tmp2 + +cleanup_milter_test14d: cleanup_milter test-queue-file14 cleanup_milter.in14d \ + cleanup_milter.ref14d1 ../postcat/postcat cleanup_milter.ref14d2 \ + cleanup_milter.reg14d + cp test-queue-file14 test-queue-file14d.tmp + chmod u+w test-queue-file14d.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in14d 2>cleanup_milter.tmp1 + diff cleanup_milter.ref14d1 cleanup_milter.tmp1 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file14d.tmp 2>/dev/null >cleanup_milter.tmp2 + diff cleanup_milter.ref14d2 cleanup_milter.tmp2 + rm -f test-queue-file14d.tmp cleanup_milter.tmp1 cleanup_milter.tmp2 + +cleanup_milter_test14e: cleanup_milter test-queue-file14 cleanup_milter.in14e \ + cleanup_milter.ref14e1 ../postcat/postcat cleanup_milter.ref14e2 \ + cleanup_milter.reg14e + cp test-queue-file14 test-queue-file14e.tmp + chmod u+w test-queue-file14e.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in14e 2>cleanup_milter.tmp1 + diff cleanup_milter.ref14e1 cleanup_milter.tmp1 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file14e.tmp 2>/dev/null >cleanup_milter.tmp2 + diff cleanup_milter.ref14e2 cleanup_milter.tmp2 + rm -f test-queue-file14e.tmp cleanup_milter.tmp1 cleanup_milter.tmp2 + +cleanup_milter_test14f: cleanup_milter test-queue-file14 cleanup_milter.in14f \ + cleanup_milter.ref14f1 ../postcat/postcat cleanup_milter.ref14f2 \ + cleanup_milter.reg14f + cp test-queue-file14 test-queue-file14f.tmp + chmod u+w test-queue-file14f.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in14f 2>cleanup_milter.tmp1 + diff cleanup_milter.ref14f1 cleanup_milter.tmp1 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file14f.tmp 2>/dev/null >cleanup_milter.tmp2 + diff cleanup_milter.ref14f2 cleanup_milter.tmp2 + rm -f test-queue-file14f.tmp cleanup_milter.tmp1 cleanup_milter.tmp2 + +cleanup_milter_test14g: cleanup_milter test-queue-file14 cleanup_milter.in14g \ + cleanup_milter.ref14g1 ../postcat/postcat cleanup_milter.ref14g2 \ + cleanup_milter.reg14g + cp test-queue-file14 test-queue-file14g.tmp + chmod u+w test-queue-file14g.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in14g 2>cleanup_milter.tmp1 + diff cleanup_milter.ref14g1 cleanup_milter.tmp1 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file14g.tmp 2>/dev/null >cleanup_milter.tmp2 + diff cleanup_milter.ref14g2 cleanup_milter.tmp2 + rm -f test-queue-file14g.tmp cleanup_milter.tmp1 cleanup_milter.tmp2 + +cleanup_milter_test15a: cleanup_milter test-queue-file15 cleanup_milter.in15a \ + cleanup_milter.ref15a1 ../postcat/postcat cleanup_milter.ref15a2 \ + cleanup_milter.reg15a + cp test-queue-file15 test-queue-file15a.tmp + chmod u+w test-queue-file15a.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in15a 2>cleanup_milter.tmp1 + diff cleanup_milter.ref15a1 cleanup_milter.tmp1 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file15a.tmp 2>/dev/null >cleanup_milter.tmp2 + diff cleanup_milter.ref15a2 cleanup_milter.tmp2 + rm -f test-queue-file15a.tmp cleanup_milter.tmp1 cleanup_milter.tmp2 + +cleanup_milter_test15b: cleanup_milter test-queue-file15 cleanup_milter.in15b \ + cleanup_milter.ref15b1 ../postcat/postcat cleanup_milter.ref15b2 \ + cleanup_milter.reg15b + cp test-queue-file15 test-queue-file15b.tmp + chmod u+w test-queue-file15b.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in15b 2>cleanup_milter.tmp1 + diff cleanup_milter.ref15b1 cleanup_milter.tmp1 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file15b.tmp 2>/dev/null >cleanup_milter.tmp2 + diff cleanup_milter.ref15b2 cleanup_milter.tmp2 + rm -f test-queue-file15b.tmp cleanup_milter.tmp1 cleanup_milter.tmp2 + +cleanup_milter_test15c: cleanup_milter test-queue-file15 cleanup_milter.in15c \ + cleanup_milter.ref15c1 ../postcat/postcat cleanup_milter.ref15c2 \ + cleanup_milter.reg15c + cp test-queue-file15 test-queue-file15c.tmp + chmod u+w test-queue-file15c.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in15c 2>cleanup_milter.tmp1 + diff cleanup_milter.ref15c1 cleanup_milter.tmp1 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file15c.tmp 2>/dev/null >cleanup_milter.tmp2 + diff cleanup_milter.ref15c2 cleanup_milter.tmp2 + rm -f test-queue-file15c.tmp cleanup_milter.tmp1 cleanup_milter.tmp2 + +cleanup_milter_test15d: cleanup_milter test-queue-file15 cleanup_milter.in15d \ + cleanup_milter.ref15d1 ../postcat/postcat cleanup_milter.ref15d2 \ + cleanup_milter.reg15d + cp test-queue-file15 test-queue-file15d.tmp + chmod u+w test-queue-file15d.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in15d 2>cleanup_milter.tmp1 + diff cleanup_milter.ref15d1 cleanup_milter.tmp1 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file15d.tmp 2>/dev/null >cleanup_milter.tmp2 + diff cleanup_milter.ref15d2 cleanup_milter.tmp2 + rm -f test-queue-file15d.tmp cleanup_milter.tmp1 cleanup_milter.tmp2 + +cleanup_milter_test15e: cleanup_milter test-queue-file15 cleanup_milter.in15e \ + cleanup_milter.ref15e1 ../postcat/postcat cleanup_milter.ref15e2 \ + cleanup_milter.reg15e + cp test-queue-file15 test-queue-file15e.tmp + chmod u+w test-queue-file15e.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in15e 2>cleanup_milter.tmp1 + diff cleanup_milter.ref15e1 cleanup_milter.tmp1 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file15e.tmp 2>/dev/null >cleanup_milter.tmp2 + diff cleanup_milter.ref15e2 cleanup_milter.tmp2 + rm -f test-queue-file15e.tmp cleanup_milter.tmp1 cleanup_milter.tmp2 + +cleanup_milter_test15f: cleanup_milter test-queue-file15 cleanup_milter.in15f \ + cleanup_milter.ref15f1 ../postcat/postcat cleanup_milter.ref15f2 \ + cleanup_milter.reg15f + cp test-queue-file15 test-queue-file15f.tmp + chmod u+w test-queue-file15f.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in15f 2>cleanup_milter.tmp1 + diff cleanup_milter.ref15f1 cleanup_milter.tmp1 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file15f.tmp 2>/dev/null >cleanup_milter.tmp2 + diff cleanup_milter.ref15f2 cleanup_milter.tmp2 + rm -f test-queue-file15f.tmp cleanup_milter.tmp1 cleanup_milter.tmp2 + +cleanup_milter_test15g: cleanup_milter test-queue-file15 cleanup_milter.in15g \ + cleanup_milter.ref15g1 ../postcat/postcat cleanup_milter.ref15g2 \ + cleanup_milter.reg15g + cp test-queue-file15 test-queue-file15g.tmp + chmod u+w test-queue-file15g.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in15g 2>cleanup_milter.tmp1 + diff cleanup_milter.ref15g1 cleanup_milter.tmp1 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file15g.tmp 2>/dev/null >cleanup_milter.tmp2 + diff cleanup_milter.ref15g2 cleanup_milter.tmp2 + rm -f test-queue-file15g.tmp cleanup_milter.tmp1 cleanup_milter.tmp2 + +cleanup_milter_test15h: cleanup_milter test-queue-file15 cleanup_milter.in15h \ + cleanup_milter.ref15h1 ../postcat/postcat cleanup_milter.ref15h2 \ + cleanup_milter.reg15h + cp test-queue-file15 test-queue-file15h.tmp + chmod u+w test-queue-file15h.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in15h 2>cleanup_milter.tmp1 + diff cleanup_milter.ref15h1 cleanup_milter.tmp1 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file15h.tmp 2>/dev/null >cleanup_milter.tmp2 + diff cleanup_milter.ref15h2 cleanup_milter.tmp2 + rm -f test-queue-file15h.tmp cleanup_milter.tmp1 cleanup_milter.tmp2 + +cleanup_milter_test15i: cleanup_milter test-queue-file15 cleanup_milter.in15i \ + cleanup_milter.ref15i1 ../postcat/postcat cleanup_milter.ref15i2 \ + cleanup_milter.reg15i + cp test-queue-file15 test-queue-file15i.tmp + chmod u+w test-queue-file15i.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in15i 2>cleanup_milter.tmp1 + diff cleanup_milter.ref15i1 cleanup_milter.tmp1 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file15i.tmp 2>/dev/null >cleanup_milter.tmp2 + diff cleanup_milter.ref15i2 cleanup_milter.tmp2 + rm -f test-queue-file15i.tmp cleanup_milter.tmp1 cleanup_milter.tmp2 + +cleanup_milter_test16a: cleanup_milter test-queue-file16 cleanup_milter.in16a \ + cleanup_milter.ref16a1 ../postcat/postcat cleanup_milter.ref16a2 + cp test-queue-file16 test-queue-file16a.tmp + chmod u+w test-queue-file16a.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in16a 2>cleanup_milter.tmp1 + diff cleanup_milter.ref16a1 cleanup_milter.tmp1 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file16a.tmp 2>/dev/null >cleanup_milter.tmp2 + diff cleanup_milter.ref16a2 cleanup_milter.tmp2 + rm -f test-queue-file16a.tmp cleanup_milter.tmp1 cleanup_milter.tmp2 + +cleanup_milter_test16b: cleanup_milter test-queue-file16 cleanup_milter.in16b \ + cleanup_milter.ref16b1 ../postcat/postcat cleanup_milter.ref16b2 + cp test-queue-file16 test-queue-file16b.tmp + chmod u+w test-queue-file16b.tmp + $(SHLIB_ENV) ./cleanup_milter <cleanup_milter.in16b 2>cleanup_milter.tmp1 + diff cleanup_milter.ref16b1 cleanup_milter.tmp1 + $(SHLIB_ENV) ../postcat/postcat -ov test-queue-file16b.tmp 2>/dev/null >cleanup_milter.tmp2 + diff cleanup_milter.ref16b2 cleanup_milter.tmp2 + rm -f test-queue-file16b.tmp cleanup_milter.tmp1 cleanup_milter.tmp2 + +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' +cleanup.o: ../../include/argv.h +cleanup.o: ../../include/attr.h +cleanup.o: ../../include/been_here.h +cleanup.o: ../../include/check_arg.h +cleanup.o: ../../include/cleanup_user.h +cleanup.o: ../../include/dict.h +cleanup.o: ../../include/dsn_mask.h +cleanup.o: ../../include/header_body_checks.h +cleanup.o: ../../include/header_opts.h +cleanup.o: ../../include/htable.h +cleanup.o: ../../include/iostuff.h +cleanup.o: ../../include/mail_conf.h +cleanup.o: ../../include/mail_params.h +cleanup.o: ../../include/mail_proto.h +cleanup.o: ../../include/mail_server.h +cleanup.o: ../../include/mail_stream.h +cleanup.o: ../../include/mail_version.h +cleanup.o: ../../include/maps.h +cleanup.o: ../../include/match_list.h +cleanup.o: ../../include/milter.h +cleanup.o: ../../include/mime_state.h +cleanup.o: ../../include/msg.h +cleanup.o: ../../include/myflock.h +cleanup.o: ../../include/mymalloc.h +cleanup.o: ../../include/nvtable.h +cleanup.o: ../../include/rec_type.h +cleanup.o: ../../include/record.h +cleanup.o: ../../include/resolve_clnt.h +cleanup.o: ../../include/string_list.h +cleanup.o: ../../include/sys_defs.h +cleanup.o: ../../include/tok822.h +cleanup.o: ../../include/vbuf.h +cleanup.o: ../../include/vstream.h +cleanup.o: ../../include/vstring.h +cleanup.o: cleanup.c +cleanup.o: cleanup.h +cleanup_addr.o: ../../include/argv.h +cleanup_addr.o: ../../include/attr.h +cleanup_addr.o: ../../include/been_here.h +cleanup_addr.o: ../../include/canon_addr.h +cleanup_addr.o: ../../include/check_arg.h +cleanup_addr.o: ../../include/cleanup_user.h +cleanup_addr.o: ../../include/dict.h +cleanup_addr.o: ../../include/dsn_mask.h +cleanup_addr.o: ../../include/ext_prop.h +cleanup_addr.o: ../../include/header_body_checks.h +cleanup_addr.o: ../../include/header_opts.h +cleanup_addr.o: ../../include/htable.h +cleanup_addr.o: ../../include/iostuff.h +cleanup_addr.o: ../../include/mail_addr.h +cleanup_addr.o: ../../include/mail_addr_find.h +cleanup_addr.o: ../../include/mail_addr_form.h +cleanup_addr.o: ../../include/mail_conf.h +cleanup_addr.o: ../../include/mail_params.h +cleanup_addr.o: ../../include/mail_proto.h +cleanup_addr.o: ../../include/mail_stream.h +cleanup_addr.o: ../../include/maps.h +cleanup_addr.o: ../../include/match_list.h +cleanup_addr.o: ../../include/milter.h +cleanup_addr.o: ../../include/mime_state.h +cleanup_addr.o: ../../include/msg.h +cleanup_addr.o: ../../include/myflock.h +cleanup_addr.o: ../../include/mymalloc.h +cleanup_addr.o: ../../include/nvtable.h +cleanup_addr.o: ../../include/rec_type.h +cleanup_addr.o: ../../include/record.h +cleanup_addr.o: ../../include/resolve_clnt.h +cleanup_addr.o: ../../include/smtputf8.h +cleanup_addr.o: ../../include/string_list.h +cleanup_addr.o: ../../include/stringops.h +cleanup_addr.o: ../../include/sys_defs.h +cleanup_addr.o: ../../include/tok822.h +cleanup_addr.o: ../../include/vbuf.h +cleanup_addr.o: ../../include/vstream.h +cleanup_addr.o: ../../include/vstring.h +cleanup_addr.o: cleanup.h +cleanup_addr.o: cleanup_addr.c +cleanup_api.o: ../../include/argv.h +cleanup_api.o: ../../include/attr.h +cleanup_api.o: ../../include/been_here.h +cleanup_api.o: ../../include/bounce.h +cleanup_api.o: ../../include/check_arg.h +cleanup_api.o: ../../include/cleanup_user.h +cleanup_api.o: ../../include/deliver_request.h +cleanup_api.o: ../../include/dict.h +cleanup_api.o: ../../include/dsn.h +cleanup_api.o: ../../include/dsn_buf.h +cleanup_api.o: ../../include/dsn_mask.h +cleanup_api.o: ../../include/header_body_checks.h +cleanup_api.o: ../../include/header_opts.h +cleanup_api.o: ../../include/htable.h +cleanup_api.o: ../../include/iostuff.h +cleanup_api.o: ../../include/mail_conf.h +cleanup_api.o: ../../include/mail_flow.h +cleanup_api.o: ../../include/mail_params.h +cleanup_api.o: ../../include/mail_proto.h +cleanup_api.o: ../../include/mail_queue.h +cleanup_api.o: ../../include/mail_stream.h +cleanup_api.o: ../../include/maps.h +cleanup_api.o: ../../include/match_list.h +cleanup_api.o: ../../include/milter.h +cleanup_api.o: ../../include/mime_state.h +cleanup_api.o: ../../include/msg.h +cleanup_api.o: ../../include/msg_stats.h +cleanup_api.o: ../../include/myflock.h +cleanup_api.o: ../../include/mymalloc.h +cleanup_api.o: ../../include/nvtable.h +cleanup_api.o: ../../include/rec_type.h +cleanup_api.o: ../../include/recipient_list.h +cleanup_api.o: ../../include/resolve_clnt.h +cleanup_api.o: ../../include/smtputf8.h +cleanup_api.o: ../../include/string_list.h +cleanup_api.o: ../../include/sys_defs.h +cleanup_api.o: ../../include/tok822.h +cleanup_api.o: ../../include/vbuf.h +cleanup_api.o: ../../include/vstream.h +cleanup_api.o: ../../include/vstring.h +cleanup_api.o: cleanup.h +cleanup_api.o: cleanup_api.c +cleanup_body_edit.o: ../../include/argv.h +cleanup_body_edit.o: ../../include/attr.h +cleanup_body_edit.o: ../../include/been_here.h +cleanup_body_edit.o: ../../include/check_arg.h +cleanup_body_edit.o: ../../include/cleanup_user.h +cleanup_body_edit.o: ../../include/dict.h +cleanup_body_edit.o: ../../include/dsn_mask.h +cleanup_body_edit.o: ../../include/header_body_checks.h +cleanup_body_edit.o: ../../include/header_opts.h +cleanup_body_edit.o: ../../include/htable.h +cleanup_body_edit.o: ../../include/mail_conf.h +cleanup_body_edit.o: ../../include/mail_stream.h +cleanup_body_edit.o: ../../include/maps.h +cleanup_body_edit.o: ../../include/match_list.h +cleanup_body_edit.o: ../../include/milter.h +cleanup_body_edit.o: ../../include/mime_state.h +cleanup_body_edit.o: ../../include/msg.h +cleanup_body_edit.o: ../../include/myflock.h +cleanup_body_edit.o: ../../include/mymalloc.h +cleanup_body_edit.o: ../../include/nvtable.h +cleanup_body_edit.o: ../../include/rec_type.h +cleanup_body_edit.o: ../../include/record.h +cleanup_body_edit.o: ../../include/resolve_clnt.h +cleanup_body_edit.o: ../../include/string_list.h +cleanup_body_edit.o: ../../include/sys_defs.h +cleanup_body_edit.o: ../../include/tok822.h +cleanup_body_edit.o: ../../include/vbuf.h +cleanup_body_edit.o: ../../include/vstream.h +cleanup_body_edit.o: ../../include/vstring.h +cleanup_body_edit.o: cleanup.h +cleanup_body_edit.o: cleanup_body_edit.c +cleanup_bounce.o: ../../include/argv.h +cleanup_bounce.o: ../../include/attr.h +cleanup_bounce.o: ../../include/been_here.h +cleanup_bounce.o: ../../include/bounce.h +cleanup_bounce.o: ../../include/check_arg.h +cleanup_bounce.o: ../../include/cleanup_user.h +cleanup_bounce.o: ../../include/deliver_request.h +cleanup_bounce.o: ../../include/dict.h +cleanup_bounce.o: ../../include/dsn.h +cleanup_bounce.o: ../../include/dsn_buf.h +cleanup_bounce.o: ../../include/dsn_mask.h +cleanup_bounce.o: ../../include/dsn_util.h +cleanup_bounce.o: ../../include/header_body_checks.h +cleanup_bounce.o: ../../include/header_opts.h +cleanup_bounce.o: ../../include/htable.h +cleanup_bounce.o: ../../include/iostuff.h +cleanup_bounce.o: ../../include/mail_conf.h +cleanup_bounce.o: ../../include/mail_params.h +cleanup_bounce.o: ../../include/mail_proto.h +cleanup_bounce.o: ../../include/mail_queue.h +cleanup_bounce.o: ../../include/mail_stream.h +cleanup_bounce.o: ../../include/maps.h +cleanup_bounce.o: ../../include/match_list.h +cleanup_bounce.o: ../../include/milter.h +cleanup_bounce.o: ../../include/mime_state.h +cleanup_bounce.o: ../../include/msg.h +cleanup_bounce.o: ../../include/msg_stats.h +cleanup_bounce.o: ../../include/myflock.h +cleanup_bounce.o: ../../include/mymalloc.h +cleanup_bounce.o: ../../include/nvtable.h +cleanup_bounce.o: ../../include/rec_attr_map.h +cleanup_bounce.o: ../../include/rec_type.h +cleanup_bounce.o: ../../include/recipient_list.h +cleanup_bounce.o: ../../include/record.h +cleanup_bounce.o: ../../include/resolve_clnt.h +cleanup_bounce.o: ../../include/string_list.h +cleanup_bounce.o: ../../include/stringops.h +cleanup_bounce.o: ../../include/sys_defs.h +cleanup_bounce.o: ../../include/tok822.h +cleanup_bounce.o: ../../include/vbuf.h +cleanup_bounce.o: ../../include/vstream.h +cleanup_bounce.o: ../../include/vstring.h +cleanup_bounce.o: cleanup.h +cleanup_bounce.o: cleanup_bounce.c +cleanup_envelope.o: ../../include/argv.h +cleanup_envelope.o: ../../include/attr.h +cleanup_envelope.o: ../../include/been_here.h +cleanup_envelope.o: ../../include/check_arg.h +cleanup_envelope.o: ../../include/cleanup_user.h +cleanup_envelope.o: ../../include/deliver_request.h +cleanup_envelope.o: ../../include/dict.h +cleanup_envelope.o: ../../include/dsn.h +cleanup_envelope.o: ../../include/dsn_mask.h +cleanup_envelope.o: ../../include/header_body_checks.h +cleanup_envelope.o: ../../include/header_opts.h +cleanup_envelope.o: ../../include/htable.h +cleanup_envelope.o: ../../include/iostuff.h +cleanup_envelope.o: ../../include/mail_conf.h +cleanup_envelope.o: ../../include/mail_params.h +cleanup_envelope.o: ../../include/mail_proto.h +cleanup_envelope.o: ../../include/mail_stream.h +cleanup_envelope.o: ../../include/maps.h +cleanup_envelope.o: ../../include/match_list.h +cleanup_envelope.o: ../../include/milter.h +cleanup_envelope.o: ../../include/mime_state.h +cleanup_envelope.o: ../../include/msg.h +cleanup_envelope.o: ../../include/msg_stats.h +cleanup_envelope.o: ../../include/myflock.h +cleanup_envelope.o: ../../include/mymalloc.h +cleanup_envelope.o: ../../include/nvtable.h +cleanup_envelope.o: ../../include/qmgr_user.h +cleanup_envelope.o: ../../include/rec_attr_map.h +cleanup_envelope.o: ../../include/rec_type.h +cleanup_envelope.o: ../../include/recipient_list.h +cleanup_envelope.o: ../../include/record.h +cleanup_envelope.o: ../../include/resolve_clnt.h +cleanup_envelope.o: ../../include/smtputf8.h +cleanup_envelope.o: ../../include/string_list.h +cleanup_envelope.o: ../../include/stringops.h +cleanup_envelope.o: ../../include/sys_defs.h +cleanup_envelope.o: ../../include/tok822.h +cleanup_envelope.o: ../../include/vbuf.h +cleanup_envelope.o: ../../include/verp_sender.h +cleanup_envelope.o: ../../include/vstream.h +cleanup_envelope.o: ../../include/vstring.h +cleanup_envelope.o: cleanup.h +cleanup_envelope.o: cleanup_envelope.c +cleanup_extracted.o: ../../include/argv.h +cleanup_extracted.o: ../../include/attr.h +cleanup_extracted.o: ../../include/been_here.h +cleanup_extracted.o: ../../include/check_arg.h +cleanup_extracted.o: ../../include/cleanup_user.h +cleanup_extracted.o: ../../include/dict.h +cleanup_extracted.o: ../../include/dsn_mask.h +cleanup_extracted.o: ../../include/header_body_checks.h +cleanup_extracted.o: ../../include/header_opts.h +cleanup_extracted.o: ../../include/htable.h +cleanup_extracted.o: ../../include/iostuff.h +cleanup_extracted.o: ../../include/mail_conf.h +cleanup_extracted.o: ../../include/mail_params.h +cleanup_extracted.o: ../../include/mail_proto.h +cleanup_extracted.o: ../../include/mail_stream.h +cleanup_extracted.o: ../../include/maps.h +cleanup_extracted.o: ../../include/match_list.h +cleanup_extracted.o: ../../include/milter.h +cleanup_extracted.o: ../../include/mime_state.h +cleanup_extracted.o: ../../include/msg.h +cleanup_extracted.o: ../../include/myflock.h +cleanup_extracted.o: ../../include/mymalloc.h +cleanup_extracted.o: ../../include/nvtable.h +cleanup_extracted.o: ../../include/qmgr_user.h +cleanup_extracted.o: ../../include/rec_attr_map.h +cleanup_extracted.o: ../../include/rec_type.h +cleanup_extracted.o: ../../include/record.h +cleanup_extracted.o: ../../include/resolve_clnt.h +cleanup_extracted.o: ../../include/string_list.h +cleanup_extracted.o: ../../include/stringops.h +cleanup_extracted.o: ../../include/sys_defs.h +cleanup_extracted.o: ../../include/tok822.h +cleanup_extracted.o: ../../include/vbuf.h +cleanup_extracted.o: ../../include/vstream.h +cleanup_extracted.o: ../../include/vstring.h +cleanup_extracted.o: cleanup.h +cleanup_extracted.o: cleanup_extracted.c +cleanup_final.o: ../../include/argv.h +cleanup_final.o: ../../include/attr.h +cleanup_final.o: ../../include/been_here.h +cleanup_final.o: ../../include/check_arg.h +cleanup_final.o: ../../include/cleanup_user.h +cleanup_final.o: ../../include/dict.h +cleanup_final.o: ../../include/dsn_mask.h +cleanup_final.o: ../../include/header_body_checks.h +cleanup_final.o: ../../include/header_opts.h +cleanup_final.o: ../../include/htable.h +cleanup_final.o: ../../include/mail_conf.h +cleanup_final.o: ../../include/mail_stream.h +cleanup_final.o: ../../include/maps.h +cleanup_final.o: ../../include/match_list.h +cleanup_final.o: ../../include/milter.h +cleanup_final.o: ../../include/mime_state.h +cleanup_final.o: ../../include/msg.h +cleanup_final.o: ../../include/myflock.h +cleanup_final.o: ../../include/mymalloc.h +cleanup_final.o: ../../include/nvtable.h +cleanup_final.o: ../../include/rec_type.h +cleanup_final.o: ../../include/resolve_clnt.h +cleanup_final.o: ../../include/string_list.h +cleanup_final.o: ../../include/sys_defs.h +cleanup_final.o: ../../include/tok822.h +cleanup_final.o: ../../include/vbuf.h +cleanup_final.o: ../../include/vstream.h +cleanup_final.o: ../../include/vstring.h +cleanup_final.o: cleanup.h +cleanup_final.o: cleanup_final.c +cleanup_init.o: ../../include/argv.h +cleanup_init.o: ../../include/attr.h +cleanup_init.o: ../../include/been_here.h +cleanup_init.o: ../../include/check_arg.h +cleanup_init.o: ../../include/cleanup_user.h +cleanup_init.o: ../../include/dict.h +cleanup_init.o: ../../include/dsn_mask.h +cleanup_init.o: ../../include/ext_prop.h +cleanup_init.o: ../../include/flush_clnt.h +cleanup_init.o: ../../include/header_body_checks.h +cleanup_init.o: ../../include/header_opts.h +cleanup_init.o: ../../include/htable.h +cleanup_init.o: ../../include/iostuff.h +cleanup_init.o: ../../include/mail_addr.h +cleanup_init.o: ../../include/mail_conf.h +cleanup_init.o: ../../include/mail_params.h +cleanup_init.o: ../../include/mail_stream.h +cleanup_init.o: ../../include/mail_version.h +cleanup_init.o: ../../include/maps.h +cleanup_init.o: ../../include/match_list.h +cleanup_init.o: ../../include/milter.h +cleanup_init.o: ../../include/mime_state.h +cleanup_init.o: ../../include/msg.h +cleanup_init.o: ../../include/myflock.h +cleanup_init.o: ../../include/mymalloc.h +cleanup_init.o: ../../include/name_code.h +cleanup_init.o: ../../include/name_mask.h +cleanup_init.o: ../../include/nvtable.h +cleanup_init.o: ../../include/resolve_clnt.h +cleanup_init.o: ../../include/string_list.h +cleanup_init.o: ../../include/stringops.h +cleanup_init.o: ../../include/sys_defs.h +cleanup_init.o: ../../include/tok822.h +cleanup_init.o: ../../include/vbuf.h +cleanup_init.o: ../../include/vstream.h +cleanup_init.o: ../../include/vstring.h +cleanup_init.o: cleanup.h +cleanup_init.o: cleanup_init.c +cleanup_map11.o: ../../include/argv.h +cleanup_map11.o: ../../include/attr.h +cleanup_map11.o: ../../include/been_here.h +cleanup_map11.o: ../../include/check_arg.h +cleanup_map11.o: ../../include/cleanup_user.h +cleanup_map11.o: ../../include/dict.h +cleanup_map11.o: ../../include/dsn_mask.h +cleanup_map11.o: ../../include/header_body_checks.h +cleanup_map11.o: ../../include/header_opts.h +cleanup_map11.o: ../../include/htable.h +cleanup_map11.o: ../../include/mail_addr_form.h +cleanup_map11.o: ../../include/mail_addr_map.h +cleanup_map11.o: ../../include/mail_conf.h +cleanup_map11.o: ../../include/mail_stream.h +cleanup_map11.o: ../../include/maps.h +cleanup_map11.o: ../../include/match_list.h +cleanup_map11.o: ../../include/milter.h +cleanup_map11.o: ../../include/mime_state.h +cleanup_map11.o: ../../include/msg.h +cleanup_map11.o: ../../include/myflock.h +cleanup_map11.o: ../../include/mymalloc.h +cleanup_map11.o: ../../include/nvtable.h +cleanup_map11.o: ../../include/quote_822_local.h +cleanup_map11.o: ../../include/quote_flags.h +cleanup_map11.o: ../../include/resolve_clnt.h +cleanup_map11.o: ../../include/string_list.h +cleanup_map11.o: ../../include/stringops.h +cleanup_map11.o: ../../include/sys_defs.h +cleanup_map11.o: ../../include/tok822.h +cleanup_map11.o: ../../include/vbuf.h +cleanup_map11.o: ../../include/vstream.h +cleanup_map11.o: ../../include/vstring.h +cleanup_map11.o: cleanup.h +cleanup_map11.o: cleanup_map11.c +cleanup_map1n.o: ../../include/argv.h +cleanup_map1n.o: ../../include/attr.h +cleanup_map1n.o: ../../include/been_here.h +cleanup_map1n.o: ../../include/check_arg.h +cleanup_map1n.o: ../../include/cleanup_user.h +cleanup_map1n.o: ../../include/dict.h +cleanup_map1n.o: ../../include/dsn_mask.h +cleanup_map1n.o: ../../include/header_body_checks.h +cleanup_map1n.o: ../../include/header_opts.h +cleanup_map1n.o: ../../include/htable.h +cleanup_map1n.o: ../../include/mail_addr_form.h +cleanup_map1n.o: ../../include/mail_addr_map.h +cleanup_map1n.o: ../../include/mail_conf.h +cleanup_map1n.o: ../../include/mail_params.h +cleanup_map1n.o: ../../include/mail_stream.h +cleanup_map1n.o: ../../include/maps.h +cleanup_map1n.o: ../../include/match_list.h +cleanup_map1n.o: ../../include/milter.h +cleanup_map1n.o: ../../include/mime_state.h +cleanup_map1n.o: ../../include/msg.h +cleanup_map1n.o: ../../include/myflock.h +cleanup_map1n.o: ../../include/mymalloc.h +cleanup_map1n.o: ../../include/nvtable.h +cleanup_map1n.o: ../../include/quote_822_local.h +cleanup_map1n.o: ../../include/quote_flags.h +cleanup_map1n.o: ../../include/resolve_clnt.h +cleanup_map1n.o: ../../include/string_list.h +cleanup_map1n.o: ../../include/stringops.h +cleanup_map1n.o: ../../include/sys_defs.h +cleanup_map1n.o: ../../include/tok822.h +cleanup_map1n.o: ../../include/vbuf.h +cleanup_map1n.o: ../../include/vstream.h +cleanup_map1n.o: ../../include/vstring.h +cleanup_map1n.o: cleanup.h +cleanup_map1n.o: cleanup_map1n.c +cleanup_masquerade.o: ../../include/argv.h +cleanup_masquerade.o: ../../include/attr.h +cleanup_masquerade.o: ../../include/been_here.h +cleanup_masquerade.o: ../../include/check_arg.h +cleanup_masquerade.o: ../../include/cleanup_user.h +cleanup_masquerade.o: ../../include/dict.h +cleanup_masquerade.o: ../../include/dsn_mask.h +cleanup_masquerade.o: ../../include/header_body_checks.h +cleanup_masquerade.o: ../../include/header_opts.h +cleanup_masquerade.o: ../../include/htable.h +cleanup_masquerade.o: ../../include/mail_conf.h +cleanup_masquerade.o: ../../include/mail_params.h +cleanup_masquerade.o: ../../include/mail_stream.h +cleanup_masquerade.o: ../../include/maps.h +cleanup_masquerade.o: ../../include/match_list.h +cleanup_masquerade.o: ../../include/milter.h +cleanup_masquerade.o: ../../include/mime_state.h +cleanup_masquerade.o: ../../include/msg.h +cleanup_masquerade.o: ../../include/myflock.h +cleanup_masquerade.o: ../../include/mymalloc.h +cleanup_masquerade.o: ../../include/nvtable.h +cleanup_masquerade.o: ../../include/quote_822_local.h +cleanup_masquerade.o: ../../include/quote_flags.h +cleanup_masquerade.o: ../../include/resolve_clnt.h +cleanup_masquerade.o: ../../include/string_list.h +cleanup_masquerade.o: ../../include/stringops.h +cleanup_masquerade.o: ../../include/sys_defs.h +cleanup_masquerade.o: ../../include/tok822.h +cleanup_masquerade.o: ../../include/vbuf.h +cleanup_masquerade.o: ../../include/vstream.h +cleanup_masquerade.o: ../../include/vstring.h +cleanup_masquerade.o: cleanup.h +cleanup_masquerade.o: cleanup_masquerade.c +cleanup_message.o: ../../include/argv.h +cleanup_message.o: ../../include/attr.h +cleanup_message.o: ../../include/been_here.h +cleanup_message.o: ../../include/check_arg.h +cleanup_message.o: ../../include/cleanup_user.h +cleanup_message.o: ../../include/conv_time.h +cleanup_message.o: ../../include/dict.h +cleanup_message.o: ../../include/dsn_mask.h +cleanup_message.o: ../../include/dsn_util.h +cleanup_message.o: ../../include/ext_prop.h +cleanup_message.o: ../../include/header_body_checks.h +cleanup_message.o: ../../include/header_opts.h +cleanup_message.o: ../../include/htable.h +cleanup_message.o: ../../include/info_log_addr_form.h +cleanup_message.o: ../../include/iostuff.h +cleanup_message.o: ../../include/is_header.h +cleanup_message.o: ../../include/lex_822.h +cleanup_message.o: ../../include/mail_addr.h +cleanup_message.o: ../../include/mail_conf.h +cleanup_message.o: ../../include/mail_date.h +cleanup_message.o: ../../include/mail_params.h +cleanup_message.o: ../../include/mail_proto.h +cleanup_message.o: ../../include/mail_stream.h +cleanup_message.o: ../../include/maps.h +cleanup_message.o: ../../include/match_list.h +cleanup_message.o: ../../include/milter.h +cleanup_message.o: ../../include/mime_state.h +cleanup_message.o: ../../include/msg.h +cleanup_message.o: ../../include/myflock.h +cleanup_message.o: ../../include/mymalloc.h +cleanup_message.o: ../../include/nvtable.h +cleanup_message.o: ../../include/quote_822_local.h +cleanup_message.o: ../../include/quote_flags.h +cleanup_message.o: ../../include/rec_type.h +cleanup_message.o: ../../include/record.h +cleanup_message.o: ../../include/resolve_clnt.h +cleanup_message.o: ../../include/split_at.h +cleanup_message.o: ../../include/string_list.h +cleanup_message.o: ../../include/stringops.h +cleanup_message.o: ../../include/sys_defs.h +cleanup_message.o: ../../include/tok822.h +cleanup_message.o: ../../include/vbuf.h +cleanup_message.o: ../../include/vstream.h +cleanup_message.o: ../../include/vstring.h +cleanup_message.o: cleanup.h +cleanup_message.o: cleanup_message.c +cleanup_milter.o: ../../include/argv.h +cleanup_milter.o: ../../include/attr.h +cleanup_milter.o: ../../include/been_here.h +cleanup_milter.o: ../../include/check_arg.h +cleanup_milter.o: ../../include/cleanup_user.h +cleanup_milter.o: ../../include/dict.h +cleanup_milter.o: ../../include/dsn_mask.h +cleanup_milter.o: ../../include/dsn_util.h +cleanup_milter.o: ../../include/header_body_checks.h +cleanup_milter.o: ../../include/header_opts.h +cleanup_milter.o: ../../include/htable.h +cleanup_milter.o: ../../include/inet_proto.h +cleanup_milter.o: ../../include/info_log_addr_form.h +cleanup_milter.o: ../../include/iostuff.h +cleanup_milter.o: ../../include/is_header.h +cleanup_milter.o: ../../include/lex_822.h +cleanup_milter.o: ../../include/mail_conf.h +cleanup_milter.o: ../../include/mail_params.h +cleanup_milter.o: ../../include/mail_proto.h +cleanup_milter.o: ../../include/mail_stream.h +cleanup_milter.o: ../../include/maps.h +cleanup_milter.o: ../../include/match_list.h +cleanup_milter.o: ../../include/milter.h +cleanup_milter.o: ../../include/mime_state.h +cleanup_milter.o: ../../include/msg.h +cleanup_milter.o: ../../include/myflock.h +cleanup_milter.o: ../../include/mymalloc.h +cleanup_milter.o: ../../include/nvtable.h +cleanup_milter.o: ../../include/off_cvt.h +cleanup_milter.o: ../../include/quote_821_local.h +cleanup_milter.o: ../../include/quote_flags.h +cleanup_milter.o: ../../include/rec_attr_map.h +cleanup_milter.o: ../../include/rec_type.h +cleanup_milter.o: ../../include/record.h +cleanup_milter.o: ../../include/resolve_clnt.h +cleanup_milter.o: ../../include/string_list.h +cleanup_milter.o: ../../include/stringops.h +cleanup_milter.o: ../../include/sys_defs.h +cleanup_milter.o: ../../include/tok822.h +cleanup_milter.o: ../../include/vbuf.h +cleanup_milter.o: ../../include/vstream.h +cleanup_milter.o: ../../include/vstring.h +cleanup_milter.o: ../../include/xtext.h +cleanup_milter.o: cleanup.h +cleanup_milter.o: cleanup_milter.c +cleanup_out.o: ../../include/argv.h +cleanup_out.o: ../../include/attr.h +cleanup_out.o: ../../include/been_here.h +cleanup_out.o: ../../include/check_arg.h +cleanup_out.o: ../../include/cleanup_user.h +cleanup_out.o: ../../include/dict.h +cleanup_out.o: ../../include/dsn_mask.h +cleanup_out.o: ../../include/header_body_checks.h +cleanup_out.o: ../../include/header_opts.h +cleanup_out.o: ../../include/htable.h +cleanup_out.o: ../../include/lex_822.h +cleanup_out.o: ../../include/mail_conf.h +cleanup_out.o: ../../include/mail_params.h +cleanup_out.o: ../../include/mail_stream.h +cleanup_out.o: ../../include/maps.h +cleanup_out.o: ../../include/match_list.h +cleanup_out.o: ../../include/milter.h +cleanup_out.o: ../../include/mime_state.h +cleanup_out.o: ../../include/msg.h +cleanup_out.o: ../../include/myflock.h +cleanup_out.o: ../../include/mymalloc.h +cleanup_out.o: ../../include/nvtable.h +cleanup_out.o: ../../include/rec_type.h +cleanup_out.o: ../../include/record.h +cleanup_out.o: ../../include/resolve_clnt.h +cleanup_out.o: ../../include/smtputf8.h +cleanup_out.o: ../../include/split_at.h +cleanup_out.o: ../../include/string_list.h +cleanup_out.o: ../../include/stringops.h +cleanup_out.o: ../../include/sys_defs.h +cleanup_out.o: ../../include/tok822.h +cleanup_out.o: ../../include/vbuf.h +cleanup_out.o: ../../include/vstream.h +cleanup_out.o: ../../include/vstring.h +cleanup_out.o: cleanup.h +cleanup_out.o: cleanup_out.c +cleanup_out_recipient.o: ../../include/argv.h +cleanup_out_recipient.o: ../../include/attr.h +cleanup_out_recipient.o: ../../include/been_here.h +cleanup_out_recipient.o: ../../include/bounce.h +cleanup_out_recipient.o: ../../include/check_arg.h +cleanup_out_recipient.o: ../../include/cleanup_user.h +cleanup_out_recipient.o: ../../include/deliver_request.h +cleanup_out_recipient.o: ../../include/dict.h +cleanup_out_recipient.o: ../../include/dsn.h +cleanup_out_recipient.o: ../../include/dsn_buf.h +cleanup_out_recipient.o: ../../include/dsn_mask.h +cleanup_out_recipient.o: ../../include/ext_prop.h +cleanup_out_recipient.o: ../../include/header_body_checks.h +cleanup_out_recipient.o: ../../include/header_opts.h +cleanup_out_recipient.o: ../../include/htable.h +cleanup_out_recipient.o: ../../include/iostuff.h +cleanup_out_recipient.o: ../../include/mail_conf.h +cleanup_out_recipient.o: ../../include/mail_params.h +cleanup_out_recipient.o: ../../include/mail_proto.h +cleanup_out_recipient.o: ../../include/mail_queue.h +cleanup_out_recipient.o: ../../include/mail_stream.h +cleanup_out_recipient.o: ../../include/maps.h +cleanup_out_recipient.o: ../../include/match_list.h +cleanup_out_recipient.o: ../../include/milter.h +cleanup_out_recipient.o: ../../include/mime_state.h +cleanup_out_recipient.o: ../../include/msg.h +cleanup_out_recipient.o: ../../include/msg_stats.h +cleanup_out_recipient.o: ../../include/myflock.h +cleanup_out_recipient.o: ../../include/mymalloc.h +cleanup_out_recipient.o: ../../include/nvtable.h +cleanup_out_recipient.o: ../../include/rec_type.h +cleanup_out_recipient.o: ../../include/recipient_list.h +cleanup_out_recipient.o: ../../include/resolve_clnt.h +cleanup_out_recipient.o: ../../include/string_list.h +cleanup_out_recipient.o: ../../include/sys_defs.h +cleanup_out_recipient.o: ../../include/tok822.h +cleanup_out_recipient.o: ../../include/trace.h +cleanup_out_recipient.o: ../../include/vbuf.h +cleanup_out_recipient.o: ../../include/verify.h +cleanup_out_recipient.o: ../../include/vstream.h +cleanup_out_recipient.o: ../../include/vstring.h +cleanup_out_recipient.o: cleanup.h +cleanup_out_recipient.o: cleanup_out_recipient.c +cleanup_region.o: ../../include/argv.h +cleanup_region.o: ../../include/attr.h +cleanup_region.o: ../../include/been_here.h +cleanup_region.o: ../../include/check_arg.h +cleanup_region.o: ../../include/cleanup_user.h +cleanup_region.o: ../../include/dict.h +cleanup_region.o: ../../include/dsn_mask.h +cleanup_region.o: ../../include/header_body_checks.h +cleanup_region.o: ../../include/header_opts.h +cleanup_region.o: ../../include/htable.h +cleanup_region.o: ../../include/mail_conf.h +cleanup_region.o: ../../include/mail_stream.h +cleanup_region.o: ../../include/maps.h +cleanup_region.o: ../../include/match_list.h +cleanup_region.o: ../../include/milter.h +cleanup_region.o: ../../include/mime_state.h +cleanup_region.o: ../../include/msg.h +cleanup_region.o: ../../include/myflock.h +cleanup_region.o: ../../include/mymalloc.h +cleanup_region.o: ../../include/nvtable.h +cleanup_region.o: ../../include/resolve_clnt.h +cleanup_region.o: ../../include/string_list.h +cleanup_region.o: ../../include/sys_defs.h +cleanup_region.o: ../../include/tok822.h +cleanup_region.o: ../../include/vbuf.h +cleanup_region.o: ../../include/vstream.h +cleanup_region.o: ../../include/vstring.h +cleanup_region.o: ../../include/warn_stat.h +cleanup_region.o: cleanup.h +cleanup_region.o: cleanup_region.c +cleanup_rewrite.o: ../../include/argv.h +cleanup_rewrite.o: ../../include/attr.h +cleanup_rewrite.o: ../../include/been_here.h +cleanup_rewrite.o: ../../include/check_arg.h +cleanup_rewrite.o: ../../include/cleanup_user.h +cleanup_rewrite.o: ../../include/dict.h +cleanup_rewrite.o: ../../include/dsn_mask.h +cleanup_rewrite.o: ../../include/header_body_checks.h +cleanup_rewrite.o: ../../include/header_opts.h +cleanup_rewrite.o: ../../include/htable.h +cleanup_rewrite.o: ../../include/iostuff.h +cleanup_rewrite.o: ../../include/mail_conf.h +cleanup_rewrite.o: ../../include/mail_proto.h +cleanup_rewrite.o: ../../include/mail_stream.h +cleanup_rewrite.o: ../../include/maps.h +cleanup_rewrite.o: ../../include/match_list.h +cleanup_rewrite.o: ../../include/milter.h +cleanup_rewrite.o: ../../include/mime_state.h +cleanup_rewrite.o: ../../include/msg.h +cleanup_rewrite.o: ../../include/myflock.h +cleanup_rewrite.o: ../../include/mymalloc.h +cleanup_rewrite.o: ../../include/nvtable.h +cleanup_rewrite.o: ../../include/quote_822_local.h +cleanup_rewrite.o: ../../include/quote_flags.h +cleanup_rewrite.o: ../../include/resolve_clnt.h +cleanup_rewrite.o: ../../include/rewrite_clnt.h +cleanup_rewrite.o: ../../include/string_list.h +cleanup_rewrite.o: ../../include/sys_defs.h +cleanup_rewrite.o: ../../include/tok822.h +cleanup_rewrite.o: ../../include/vbuf.h +cleanup_rewrite.o: ../../include/vstream.h +cleanup_rewrite.o: ../../include/vstring.h +cleanup_rewrite.o: cleanup.h +cleanup_rewrite.o: cleanup_rewrite.c +cleanup_state.o: ../../include/argv.h +cleanup_state.o: ../../include/attr.h +cleanup_state.o: ../../include/been_here.h +cleanup_state.o: ../../include/check_arg.h +cleanup_state.o: ../../include/cleanup_user.h +cleanup_state.o: ../../include/dict.h +cleanup_state.o: ../../include/dsn_mask.h +cleanup_state.o: ../../include/header_body_checks.h +cleanup_state.o: ../../include/header_opts.h +cleanup_state.o: ../../include/htable.h +cleanup_state.o: ../../include/iostuff.h +cleanup_state.o: ../../include/mail_conf.h +cleanup_state.o: ../../include/mail_params.h +cleanup_state.o: ../../include/mail_proto.h +cleanup_state.o: ../../include/mail_stream.h +cleanup_state.o: ../../include/maps.h +cleanup_state.o: ../../include/match_list.h +cleanup_state.o: ../../include/milter.h +cleanup_state.o: ../../include/mime_state.h +cleanup_state.o: ../../include/myflock.h +cleanup_state.o: ../../include/mymalloc.h +cleanup_state.o: ../../include/nvtable.h +cleanup_state.o: ../../include/resolve_clnt.h +cleanup_state.o: ../../include/string_list.h +cleanup_state.o: ../../include/sys_defs.h +cleanup_state.o: ../../include/tok822.h +cleanup_state.o: ../../include/vbuf.h +cleanup_state.o: ../../include/vstream.h +cleanup_state.o: ../../include/vstring.h +cleanup_state.o: cleanup.h +cleanup_state.o: cleanup_state.c diff --git a/src/cleanup/bug1.file b/src/cleanup/bug1.file Binary files differnew file mode 100644 index 0000000..8412ae3 --- /dev/null +++ b/src/cleanup/bug1.file diff --git a/src/cleanup/bug1.file.ref b/src/cleanup/bug1.file.ref Binary files differnew file mode 100755 index 0000000..229d26d --- /dev/null +++ b/src/cleanup/bug1.file.ref diff --git a/src/cleanup/bug1.in b/src/cleanup/bug1.in new file mode 100644 index 0000000..bda18cf --- /dev/null +++ b/src/cleanup/bug1.in @@ -0,0 +1,41 @@ +#verbose on +open bug1.file.tmp + +# Symptom: +# +# infinite loop in postcat and in delivery agents +# +# Cause: +# +# Failure to update location info after following a pointer record, +# while updating a message header record +# +# Analysis: +# +# This happens with repeated updates of the same message header. +# After the first update, the update #1 header record sits in the +# heap at the end of the queue file, and is followed by a reverse +# pointer to the start of the next message header record or the +# message body, somewhere in the middle of the queue file. +# +# The problem started with update #2 of that same message header. +# While following the reverse pointer record after the update #1 +# header record to find out the start of the next header or message +# body, the header updating routine did not update its notion of +# where it was. Thus, it believed that the next header or body record +# was located after the reverse pointer record. That was not the +# middle of the message, but the end of the queue file. The second +# update would result in an update #2 header record, followed by a +# reverse pointer to what used to be the end of the queue file, but +# had meanwhile become the location of the update #2 header record. +# +# Thus, anything that tried to deliver mail would loop on the update +# #2 header record. After update update #3 of the same header, the +# delivery agent would loop on the update #3 record, etc. + +upd_header 1 Subject long header text +upd_header 1 Subject long header text +upd_header 1 Subject long header text +upd_header 1 Subject long header text + +close diff --git a/src/cleanup/bug1.ref b/src/cleanup/bug1.ref new file mode 100644 index 0000000..362d2cb --- /dev/null +++ b/src/cleanup/bug1.ref @@ -0,0 +1,56 @@ +*** ENVELOPE RECORDS bug1.file.tmp *** + 0 message_size: 441 813 3 0 441 + 81 message_arrival_time: Sat Jan 20 19:52:41 2007 + 100 create_time: Sat Jan 20 19:52:47 2007 + 124 named_attribute: rewrite_context=local + 147 sender: wietse@porcupine.org + 169 named_attribute: log_client_name=hades.porcupine.org + 206 named_attribute: log_client_address=168.100.189.10 + 241 named_attribute: log_message_origin=hades.porcupine.org[168.100.189.10] + 297 named_attribute: log_helo_name=hades.porcupine.org + 332 named_attribute: log_protocol_name=SMTP + 356 named_attribute: client_name=hades.porcupine.org + 389 named_attribute: reverse_client_name=hades.porcupine.org + 430 named_attribute: client_address=168.100.189.10 + 461 named_attribute: helo_name=hades.porcupine.org + 492 named_attribute: client_address_type=2 + 515 named_attribute: dsn_orig_rcpt=rfc822;wietse@porcupine.org + 558 original_recipient: wietse@porcupine.org + 580 recipient: wietse@porcupine.org + 602 named_attribute: dsn_orig_rcpt=rfc822;alias@hades.porcupine.org + 650 original_recipient: alias@hades.porcupine.org + 677 recipient: wietse@porcupine.org + 699 named_attribute: dsn_orig_rcpt=rfc822;alias@hades.porcupine.org + 747 original_recipient: alias@hades.porcupine.org + 774 recipient: root@porcupine.org + 794 pointer_record: 0 + 811 *** MESSAGE CONTENTS bug1.file.tmp *** + 813 regular_text: Received: from hades.porcupine.org (hades.porcupine.org [168.100.189.10]) + 888 regular_text: by hades.porcupine.org (Postfix) with SMTP id 38132290405; + 949 regular_text: Sat, 20 Jan 2007 19:52:41 -0500 (EST) + 989 regular_text: X: 1 + 995 padding: 0 + 1006 regular_text: 2 + 1010 regular_text: 3 + 1014 regular_text: 4 + 1018 regular_text: 5 + 1022 regular_text: 6 + 1026 regular_text: 7 + 1030 regular_text: Y: 1234567 + 1042 padding: 0 + 1047 regular_text: Message-Id: <20070121005247.38132290405@hades.porcupine.org> + 1109 regular_text: Date: Sat, 20 Jan 2007 19:52:41 -0500 (EST) + 1154 regular_text: From: wietse@porcupine.org + 1182 regular_text: To: undisclosed-recipients:; + 1212 pointer_record: 1258 + 1258 pointer_record: 1302 + 1302 pointer_record: 1346 + 1346 pointer_record: 1390 + 1390 regular_text: Subject: long header text + 1417 pointer_record: 1285 + 1285 pointer_record: 1229 + 1229 regular_text: + 1231 regular_text: text + 1237 pointer_record: 0 + 1254 *** HEADER EXTRACTED bug1.file.tmp *** + 1256 *** MESSAGE FILE END bug1.file.tmp *** diff --git a/src/cleanup/bug1.text.ref b/src/cleanup/bug1.text.ref new file mode 100644 index 0000000..72fe3df --- /dev/null +++ b/src/cleanup/bug1.text.ref @@ -0,0 +1,46 @@ +*** ENVELOPE RECORDS bug1.file.tmp *** +message_size: 441 813 3 0 441 +message_arrival_time: Sat Jan 20 19:52:41 2007 +create_time: Sat Jan 20 19:52:47 2007 +named_attribute: rewrite_context=local +sender: wietse@porcupine.org +named_attribute: log_client_name=hades.porcupine.org +named_attribute: log_client_address=168.100.189.10 +named_attribute: log_message_origin=hades.porcupine.org[168.100.189.10] +named_attribute: log_helo_name=hades.porcupine.org +named_attribute: log_protocol_name=SMTP +named_attribute: client_name=hades.porcupine.org +named_attribute: reverse_client_name=hades.porcupine.org +named_attribute: client_address=168.100.189.10 +named_attribute: helo_name=hades.porcupine.org +named_attribute: client_address_type=2 +named_attribute: dsn_orig_rcpt=rfc822;wietse@porcupine.org +original_recipient: wietse@porcupine.org +recipient: wietse@porcupine.org +named_attribute: dsn_orig_rcpt=rfc822;alias@hades.porcupine.org +original_recipient: alias@hades.porcupine.org +recipient: wietse@porcupine.org +named_attribute: dsn_orig_rcpt=rfc822;alias@hades.porcupine.org +original_recipient: alias@hades.porcupine.org +recipient: root@porcupine.org +*** MESSAGE CONTENTS bug1.file.tmp *** +Received: from hades.porcupine.org (hades.porcupine.org [168.100.189.10]) + by hades.porcupine.org (Postfix) with SMTP id 38132290405; + Sat, 20 Jan 2007 19:52:41 -0500 (EST) +X: 1 + 2 + 3 + 4 + 5 + 6 + 7 +Y: 1234567 +Message-Id: <20070121005247.38132290405@hades.porcupine.org> +Date: Sat, 20 Jan 2007 19:52:41 -0500 (EST) +From: wietse@porcupine.org +To: undisclosed-recipients:; +Subject: long header text + +text +*** HEADER EXTRACTED bug1.file.tmp *** +*** MESSAGE FILE END bug1.file.tmp *** diff --git a/src/cleanup/bug2.file b/src/cleanup/bug2.file Binary files differnew file mode 100644 index 0000000..27a9ec7 --- /dev/null +++ b/src/cleanup/bug2.file diff --git a/src/cleanup/bug2.in b/src/cleanup/bug2.in new file mode 100644 index 0000000..138ca3d --- /dev/null +++ b/src/cleanup/bug2.in @@ -0,0 +1,37 @@ +#verbose on +open bug2.file.tmp + +# Two bugs while updating a short Subject: header immediately before +# a still virgin "append header" pointer record. +# +# Symptom: +# +# warning: <filename>: malformed pointer record value: <garbage> +# +# Cause: +# +# Failure to recognize the "append header" record while updating +# a short message header +# +# Analysis: +# +# This happened while updating a header record that was followed by +# the current "append header" record. The pointer could be the initial +# "append header" record between message header and body, or it could +# be a later version of that pointer somewhere in the heap. +# +# - Postfix considered the pointer record as any pointer record after +# a header record. Thus, it decided that some portion of the pointer +# record could be overwritten with the location of the new Subject: +# header on the heap. Later "append header" operations would then +# update old "append header" record and thus clobber part of the +# pointer to the new Subject: header value. +# +# - While saving the "append header" pointer record value on the +# heap, Postfix did not replace the still virgin "0" append header" +# pointer record value by the actual location of the message body +# content. + +upd_header 1 Subject hey! +add_header foo foobar +close diff --git a/src/cleanup/bug2.ref b/src/cleanup/bug2.ref new file mode 100644 index 0000000..6a0aab4 --- /dev/null +++ b/src/cleanup/bug2.ref @@ -0,0 +1,30 @@ +*** ENVELOPE RECORDS bug2.file.tmp *** + 0 message_size: 332 199 1 0 332 + 81 message_arrival_time: Sat Jan 20 20:53:54 2007 + 100 create_time: Sat Jan 20 20:53:59 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: me@porcupine.org + 180 pointer_record: 0 + 197 *** MESSAGE CONTENTS bug2.file.tmp *** + 199 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 261 regular_text: id B85F1290407; Sat, 20 Jan 2007 20:53:59 -0500 (EST) + 317 regular_text: From: me@porcupine.org + 341 regular_text: To: you@porcupine.org + 364 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 426 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 471 pointer_record: 573 + 573 regular_text: Subject: hey! + 588 padding: 0 + 591 pointer_record: 489 + 489 pointer_record: 608 + 608 regular_text: foo: foobar + 621 padding: 0 + 625 pointer_record: 506 + 506 regular_text: + 508 regular_text: text + 514 pointer_record: 0 + 531 *** HEADER EXTRACTED bug2.file.tmp *** + 533 original_recipient: you@porcupine.org + 552 recipient: you@porcupine.org + 571 *** MESSAGE FILE END bug2.file.tmp *** diff --git a/src/cleanup/bug2.text.ref b/src/cleanup/bug2.text.ref new file mode 100644 index 0000000..fd5cfe1 --- /dev/null +++ b/src/cleanup/bug2.text.ref @@ -0,0 +1,22 @@ +*** ENVELOPE RECORDS bug2.file.tmp *** +message_size: 332 199 1 0 332 +message_arrival_time: Sat Jan 20 20:53:54 2007 +create_time: Sat Jan 20 20:53:59 2007 +named_attribute: rewrite_context=local +sender_fullname: Wietse Venema +sender: me@porcupine.org +*** MESSAGE CONTENTS bug2.file.tmp *** +Received: by hades.porcupine.org (Postfix, from userid 1001) + id B85F1290407; Sat, 20 Jan 2007 20:53:59 -0500 (EST) +From: me@porcupine.org +To: you@porcupine.org +Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> +Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) +Subject: hey! +foo: foobar + +text +*** HEADER EXTRACTED bug2.file.tmp *** +original_recipient: you@porcupine.org +recipient: you@porcupine.org +*** MESSAGE FILE END bug2.file.tmp *** diff --git a/src/cleanup/bug3.file b/src/cleanup/bug3.file Binary files differnew file mode 100644 index 0000000..fd9236c --- /dev/null +++ b/src/cleanup/bug3.file diff --git a/src/cleanup/bug3.in b/src/cleanup/bug3.in new file mode 100644 index 0000000..1920eaf --- /dev/null +++ b/src/cleanup/bug3.in @@ -0,0 +1,29 @@ +#verbose on +open bug3.file.tmp + +# This was a problem with a length check in the wrong place, causing +# a short header name to match a longer one. After successful +# substring match, the "change header" code checked the length of +# the header name that was found, instead of the header name that +# was wanted. + +#add_header X-SpamTest-Envelope-From wietse@porcupine.org +#upd_header 1 X-SpamTest-Envelope-From wietse@porcupine.org +#add_header X-SpamTest-Group-ID 00000000 +#upd_header 1 X-SpamTest-Group-ID 00000000 +#add_header X-SpamTest-Info Profiles 29362 [Feb 02 2012] +#upd_header 1 X-SpamTest-Info Profiles 29362 [Feb 02 2012] +#add_header X-SpamTest-Method none +#upd_header 1 X-SpamTest-Method none +#add_header X-SpamTest-Rate 0 +#upd_header 1 X-SpamTest-Rate 0 +#add_header X-SpamTest-SPF none +#upd_header 1 X-SpamTest-SPF none +add_header X-SpamTest-Status Not detected +#upd_header 1 X-SpamTest-Status Not detected +add_header X-SpamTest-Status-Extended not_detected +upd_header 1 X-SpamTest-Status-Extended not_detected +#add_header X-SpamTest-Version SMTP-Filter Version 3.0.0 [0284], KAS30/Release +#upd_header 1 X-SpamTest-Version SMTP-Filter Version 3.0.0 [0284], KAS30/Release + +close diff --git a/src/cleanup/bug3.ref b/src/cleanup/bug3.ref new file mode 100644 index 0000000..da4d162 --- /dev/null +++ b/src/cleanup/bug3.ref @@ -0,0 +1,29 @@ +*** ENVELOPE RECORDS bug3.file.tmp *** + 0 message_size: 307 237 1 0 307 + 81 message_arrival_time: Thu Feb 2 09:02:07 2012 + 100 create_time: Thu Feb 2 09:02:07 2012 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: me@porcupine.org + 180 original_recipient: you@porcupine.org + 199 recipient: you@porcupine.org + 218 pointer_record: 0 + 235 *** MESSAGE CONTENTS bug3.file.tmp *** + 237 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 299 regular_text: id 9210192461E; Thu, 2 Feb 2012 09:02:07 -0500 (EST) + 355 regular_text: Message-Id: <20120202140207.9210192461E@hades.porcupine.org> + 417 regular_text: Date: Thu, 2 Feb 2012 09:02:07 -0500 (EST) + 462 regular_text: From: me@porcupine.org (Wietse Venema) + 502 pointer_record: 565 + 565 regular_text: X-SpamTest-Status: Not detected + 598 pointer_record: 615 + 615 pointer_record: 674 + 674 regular_text: X-SpamTest-Status-Extended: not_detected + 716 pointer_record: 657 + 657 pointer_record: 519 + 519 regular_text: + 521 regular_text: test + 527 pointer_record: 0 + 544 *** HEADER EXTRACTED bug3.file.tmp *** + 546 pointer_record: 0 + 563 *** MESSAGE FILE END bug3.file.tmp *** diff --git a/src/cleanup/bug3.text.ref b/src/cleanup/bug3.text.ref new file mode 100644 index 0000000..9e672b8 --- /dev/null +++ b/src/cleanup/bug3.text.ref @@ -0,0 +1,21 @@ +*** ENVELOPE RECORDS bug3.file.tmp *** +message_size: 307 237 1 0 307 +message_arrival_time: Thu Feb 2 09:02:07 2012 +create_time: Thu Feb 2 09:02:07 2012 +named_attribute: rewrite_context=local +sender_fullname: Wietse Venema +sender: me@porcupine.org +original_recipient: you@porcupine.org +recipient: you@porcupine.org +*** MESSAGE CONTENTS bug3.file.tmp *** +Received: by hades.porcupine.org (Postfix, from userid 1001) + id 9210192461E; Thu, 2 Feb 2012 09:02:07 -0500 (EST) +Message-Id: <20120202140207.9210192461E@hades.porcupine.org> +Date: Thu, 2 Feb 2012 09:02:07 -0500 (EST) +From: me@porcupine.org (Wietse Venema) +X-SpamTest-Status: Not detected +X-SpamTest-Status-Extended: not_detected + +test +*** HEADER EXTRACTED bug3.file.tmp *** +*** MESSAGE FILE END bug3.file.tmp *** diff --git a/src/cleanup/cleanup.c b/src/cleanup/cleanup.c new file mode 100644 index 0000000..d076e1f --- /dev/null +++ b/src/cleanup/cleanup.c @@ -0,0 +1,635 @@ +/*++ +/* NAME +/* cleanup 8 +/* SUMMARY +/* canonicalize and enqueue Postfix message +/* SYNOPSIS +/* \fBcleanup\fR [generic Postfix daemon options] +/* DESCRIPTION +/* The \fBcleanup\fR(8) daemon processes inbound mail, inserts it +/* into the \fBincoming\fR mail queue, and informs the queue +/* manager of its arrival. +/* +/* The \fBcleanup\fR(8) daemon always performs the following transformations: +/* .IP \(bu +/* Insert missing message headers: (\fBResent-\fR) \fBFrom:\fR, +/* \fBTo:\fR, \fBMessage-Id:\fR, and \fBDate:\fR. +/* .IP \(bu +/* Transform envelope and header addresses to the standard +/* \fIuser@fully-qualified-domain\fR form that is expected by other +/* Postfix programs. +/* This task is delegated to the \fBtrivial-rewrite\fR(8) daemon. +/* .IP \(bu +/* Eliminate duplicate envelope recipient addresses. +/* .IP \(bu +/* Remove message headers: \fBBcc\fR, \fBContent-Length\fR, +/* \fBResent-Bcc\fR, \fBReturn-Path\fR. +/* .PP +/* The following address transformations are optional: +/* .IP \(bu +/* Optionally, rewrite all envelope and header addresses according +/* to the mappings specified in the \fBcanonical\fR(5) lookup tables. +/* .IP \(bu +/* Optionally, masquerade envelope sender addresses and message +/* header addresses (i.e. strip host or domain information below +/* all domains listed in the \fBmasquerade_domains\fR parameter, +/* except for user names listed in \fBmasquerade_exceptions\fR). +/* By default, address masquerading does not affect envelope recipients. +/* .IP \(bu +/* Optionally, expand envelope recipients according to information +/* found in the \fBvirtual\fR(5) lookup tables. +/* .PP +/* The \fBcleanup\fR(8) daemon performs sanity checks on the content of +/* each message. When it finds a problem, by default it returns a +/* diagnostic status to the client, and leaves it up to the client +/* to deal with the problem. Alternatively, the client can request +/* the \fBcleanup\fR(8) daemon to bounce the message back to the sender +/* in case of trouble. +/* STANDARDS +/* RFC 822 (ARPA Internet Text Messages) +/* RFC 2045 (MIME: Format of Internet Message Bodies) +/* RFC 2046 (MIME: Media Types) +/* RFC 2822 (Internet Message Format) +/* RFC 3463 (Enhanced Status Codes) +/* RFC 3464 (Delivery status notifications) +/* RFC 5322 (Internet Message Format) +/* DIAGNOSTICS +/* Problems and transactions are logged to \fBsyslogd\fR(8) +/* or \fBpostlogd\fR(8). +/* BUGS +/* Table-driven rewriting rules make it hard to express \fBif then +/* else\fR and other logical relationships. +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* Changes to \fBmain.cf\fR are picked up automatically, as +/* \fBcleanup\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 "\fBundisclosed_recipients_header (see 'postconf -d' output)\fR" +/* Message header that the Postfix \fBcleanup\fR(8) server inserts when a +/* message contains no To: or Cc: message header. +/* .PP +/* Available in Postfix version 2.1 only: +/* .IP "\fBenable_errors_to (no)\fR" +/* Report mail delivery errors to the address specified with the +/* non-standard Errors-To: message header, instead of the envelope +/* sender address (this feature is removed with Postfix version 2.2, is +/* turned off by default with Postfix version 2.1, and is always turned on +/* with older Postfix versions). +/* .PP +/* Available in Postfix version 2.6 and later: +/* .IP "\fBalways_add_missing_headers (no)\fR" +/* Always add (Resent-) From:, To:, Date: or Message-ID: headers +/* when not present. +/* .PP +/* Available in Postfix version 2.9 and later: +/* .IP "\fBenable_long_queue_ids (no)\fR" +/* Enable long, non-repeating, queue IDs (queue file names). +/* .PP +/* Available in Postfix version 3.0 and later: +/* .IP "\fBmessage_drop_headers (bcc, content-length, resent-bcc, return-path)\fR" +/* Names of message headers that the \fBcleanup\fR(8) daemon will remove +/* after applying \fBheader_checks\fR(5) and before invoking Milter applications. +/* BUILT-IN CONTENT FILTERING CONTROLS +/* .ad +/* .fi +/* Postfix built-in content filtering is meant to stop a flood of +/* worms or viruses. It is not a general content filter. +/* .IP "\fBbody_checks (empty)\fR" +/* Optional lookup tables for content inspection as specified in +/* the \fBbody_checks\fR(5) manual page. +/* .IP "\fBheader_checks (empty)\fR" +/* Optional lookup tables for content inspection of primary non-MIME +/* message headers, as specified in the \fBheader_checks\fR(5) manual page. +/* .PP +/* Available in Postfix version 2.0 and later: +/* .IP "\fBbody_checks_size_limit (51200)\fR" +/* How much text in a message body segment (or attachment, if you +/* prefer to use that term) is subjected to body_checks inspection. +/* .IP "\fBmime_header_checks ($header_checks)\fR" +/* Optional lookup tables for content inspection of MIME related +/* message headers, as described in the \fBheader_checks\fR(5) manual page. +/* .IP "\fBnested_header_checks ($header_checks)\fR" +/* Optional lookup tables for content inspection of non-MIME message +/* headers in attached messages, as described in the \fBheader_checks\fR(5) +/* manual page. +/* .PP +/* Available in Postfix version 2.3 and later: +/* .IP "\fBmessage_reject_characters (empty)\fR" +/* The set of characters that Postfix will reject in message +/* content. +/* .IP "\fBmessage_strip_characters (empty)\fR" +/* The set of characters that Postfix will remove from message +/* content. +/* .PP +/* Available in Postfix version 3.9, 3.8.5, 3.7.10, 3.6.14, +/* 3.5.24, and later: +/* .IP "\fBcleanup_replace_stray_cr_lf (yes)\fR" +/* Replace each stray <CR> or <LF> character in message +/* content with a space character, to prevent outbound SMTP smuggling, +/* and to make the evaluation of Postfix-added DKIM or other signatures +/* independent from how a remote mail server handles such characters. +/* BEFORE QUEUE MILTER CONTROLS +/* .ad +/* .fi +/* As of version 2.3, Postfix supports the Sendmail version 8 +/* Milter (mail filter) protocol. When mail is not received via +/* the smtpd(8) server, the cleanup(8) server will simulate +/* SMTP events to the extent that this is possible. For details +/* see the MILTER_README document. +/* .IP "\fBnon_smtpd_milters (empty)\fR" +/* A list of Milter (mail filter) applications for new mail that +/* does not arrive via the Postfix \fBsmtpd\fR(8) server. +/* .IP "\fBmilter_protocol (6)\fR" +/* The mail filter protocol version and optional protocol extensions +/* for communication with a Milter application; prior to Postfix 2.6 +/* the default protocol is 2. +/* .IP "\fBmilter_default_action (tempfail)\fR" +/* The default action when a Milter (mail filter) application is +/* unavailable or mis-configured. +/* .IP "\fBmilter_macro_daemon_name ($myhostname)\fR" +/* The {daemon_name} macro value for Milter (mail filter) applications. +/* .IP "\fBmilter_macro_v ($mail_name $mail_version)\fR" +/* The {v} macro value for Milter (mail filter) applications. +/* .IP "\fBmilter_connect_timeout (30s)\fR" +/* The time limit for connecting to a Milter (mail filter) +/* application, and for negotiating protocol options. +/* .IP "\fBmilter_command_timeout (30s)\fR" +/* The time limit for sending an SMTP command to a Milter (mail +/* filter) application, and for receiving the response. +/* .IP "\fBmilter_content_timeout (300s)\fR" +/* The time limit for sending message content to a Milter (mail +/* filter) application, and for receiving the response. +/* .IP "\fBmilter_connect_macros (see 'postconf -d' output)\fR" +/* The macros that are sent to Milter (mail filter) applications +/* after completion of an SMTP connection. +/* .IP "\fBmilter_helo_macros (see 'postconf -d' output)\fR" +/* The macros that are sent to Milter (mail filter) applications +/* after the SMTP HELO or EHLO command. +/* .IP "\fBmilter_mail_macros (see 'postconf -d' output)\fR" +/* The macros that are sent to Milter (mail filter) applications +/* after the SMTP MAIL FROM command. +/* .IP "\fBmilter_rcpt_macros (see 'postconf -d' output)\fR" +/* The macros that are sent to Milter (mail filter) applications +/* after the SMTP RCPT TO command. +/* .IP "\fBmilter_data_macros (see 'postconf -d' output)\fR" +/* The macros that are sent to version 4 or higher Milter (mail +/* filter) applications after the SMTP DATA command. +/* .IP "\fBmilter_unknown_command_macros (see 'postconf -d' output)\fR" +/* The macros that are sent to version 3 or higher Milter (mail +/* filter) applications after an unknown SMTP command. +/* .IP "\fBmilter_end_of_data_macros (see 'postconf -d' output)\fR" +/* The macros that are sent to Milter (mail filter) applications +/* after the message end-of-data. +/* .PP +/* Available in Postfix version 2.5 and later: +/* .IP "\fBmilter_end_of_header_macros (see 'postconf -d' output)\fR" +/* The macros that are sent to Milter (mail filter) applications +/* after the end of the message header. +/* .PP +/* Available in Postfix version 2.7 and later: +/* .IP "\fBmilter_header_checks (empty)\fR" +/* Optional lookup tables for content inspection of message headers +/* that are produced by Milter applications. +/* .PP +/* Available in Postfix version 3.1 and later: +/* .IP "\fBmilter_macro_defaults (empty)\fR" +/* Optional list of \fIname=value\fR pairs that specify default +/* values for arbitrary macros that Postfix may send to Milter +/* applications. +/* MIME PROCESSING CONTROLS +/* .ad +/* .fi +/* Available in Postfix version 2.0 and later: +/* .IP "\fBdisable_mime_input_processing (no)\fR" +/* Turn off MIME processing while receiving mail. +/* .IP "\fBmime_boundary_length_limit (2048)\fR" +/* The maximal length of MIME multipart boundary strings. +/* .IP "\fBmime_nesting_limit (100)\fR" +/* The maximal recursion level that the MIME processor will handle. +/* .IP "\fBstrict_8bitmime (no)\fR" +/* Enable both strict_7bit_headers and strict_8bitmime_body. +/* .IP "\fBstrict_7bit_headers (no)\fR" +/* Reject mail with 8-bit text in message headers. +/* .IP "\fBstrict_8bitmime_body (no)\fR" +/* Reject 8-bit message body text without 8-bit MIME content encoding +/* information. +/* .IP "\fBstrict_mime_encoding_domain (no)\fR" +/* Reject mail with invalid Content-Transfer-Encoding: information +/* for the message/* or multipart/* MIME content types. +/* .PP +/* Available in Postfix version 2.5 and later: +/* .IP "\fBdetect_8bit_encoding_header (yes)\fR" +/* Automatically detect 8BITMIME body content by looking at +/* Content-Transfer-Encoding: message headers; historically, this +/* behavior was hard-coded to be "always on". +/* AUTOMATIC BCC RECIPIENT CONTROLS +/* .ad +/* .fi +/* Postfix can automatically add BCC (blind carbon copy) +/* when mail enters the mail system: +/* .IP "\fBalways_bcc (empty)\fR" +/* Optional address that receives a "blind carbon copy" of each message +/* that is received by the Postfix mail system. +/* .PP +/* Available in Postfix version 2.1 and later: +/* .IP "\fBsender_bcc_maps (empty)\fR" +/* Optional BCC (blind carbon-copy) address lookup tables, indexed +/* by sender address. +/* .IP "\fBrecipient_bcc_maps (empty)\fR" +/* Optional BCC (blind carbon-copy) address lookup tables, indexed by +/* recipient address. +/* ADDRESS TRANSFORMATION CONTROLS +/* .ad +/* .fi +/* Address rewriting is delegated to the \fBtrivial-rewrite\fR(8) daemon. +/* The \fBcleanup\fR(8) server implements table driven address mapping. +/* .IP "\fBempty_address_recipient (MAILER-DAEMON)\fR" +/* The recipient of mail addressed to the null address. +/* .IP "\fBcanonical_maps (empty)\fR" +/* Optional address mapping lookup tables for message headers and +/* envelopes. +/* .IP "\fBrecipient_canonical_maps (empty)\fR" +/* Optional address mapping lookup tables for envelope and header +/* recipient addresses. +/* .IP "\fBsender_canonical_maps (empty)\fR" +/* Optional address mapping lookup tables for envelope and header +/* sender addresses. +/* .IP "\fBmasquerade_classes (envelope_sender, header_sender, header_recipient)\fR" +/* What addresses are subject to address masquerading. +/* .IP "\fBmasquerade_domains (empty)\fR" +/* Optional list of domains whose subdomain structure will be stripped +/* off in email addresses. +/* .IP "\fBmasquerade_exceptions (empty)\fR" +/* Optional list of user names that are not subjected to address +/* masquerading, even when their addresses match $masquerade_domains. +/* .IP "\fBpropagate_unmatched_extensions (canonical, virtual)\fR" +/* What address lookup tables copy an address extension from the lookup +/* key to the lookup result. +/* .PP +/* Available before Postfix version 2.0: +/* .IP "\fBvirtual_maps (empty)\fR" +/* Optional lookup tables with a) names of domains for which all +/* addresses are aliased to addresses in other local or remote domains, +/* and b) addresses that are aliased to addresses in other local or +/* remote domains. +/* .PP +/* Available in Postfix version 2.0 and later: +/* .IP "\fBvirtual_alias_maps ($virtual_maps)\fR" +/* Optional lookup tables that alias specific mail addresses or domains +/* to other local or remote address. +/* .PP +/* Available in Postfix version 2.2 and later: +/* .IP "\fBcanonical_classes (envelope_sender, envelope_recipient, header_sender, header_recipient)\fR" +/* What addresses are subject to canonical_maps address mapping. +/* .IP "\fBrecipient_canonical_classes (envelope_recipient, header_recipient)\fR" +/* What addresses are subject to recipient_canonical_maps address +/* mapping. +/* .IP "\fBsender_canonical_classes (envelope_sender, header_sender)\fR" +/* What addresses are subject to sender_canonical_maps address +/* mapping. +/* .IP "\fBremote_header_rewrite_domain (empty)\fR" +/* Don't rewrite message headers from remote clients at all when +/* this parameter is empty; otherwise, rewrite message headers and +/* append the specified domain name to incomplete addresses. +/* RESOURCE AND RATE CONTROLS +/* .ad +/* .fi +/* .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 "\fBheader_size_limit (102400)\fR" +/* The maximal amount of memory in bytes for storing a message header. +/* .IP "\fBhopcount_limit (50)\fR" +/* The maximal number of Received: message headers that is allowed +/* in the primary message headers. +/* .IP "\fBin_flow_delay (1s)\fR" +/* Time to pause before accepting a new message, when the message +/* arrival rate exceeds the message delivery rate. +/* .IP "\fBmessage_size_limit (10240000)\fR" +/* The maximal size in bytes of a message, including envelope information. +/* .PP +/* Available in Postfix version 2.0 and later: +/* .IP "\fBheader_address_token_limit (10240)\fR" +/* The maximal number of address tokens are allowed in an address +/* message header. +/* .IP "\fBmime_boundary_length_limit (2048)\fR" +/* The maximal length of MIME multipart boundary strings. +/* .IP "\fBmime_nesting_limit (100)\fR" +/* The maximal recursion level that the MIME processor will handle. +/* .IP "\fBqueue_file_attribute_count_limit (100)\fR" +/* The maximal number of (name=value) attributes that may be stored +/* in a Postfix queue file. +/* .PP +/* Available in Postfix version 2.1 and later: +/* .IP "\fBvirtual_alias_expansion_limit (1000)\fR" +/* The maximal number of addresses that virtual alias expansion produces +/* from each original recipient. +/* .IP "\fBvirtual_alias_recursion_limit (1000)\fR" +/* The maximal nesting depth of virtual alias expansion. +/* .PP +/* Available in Postfix version 3.0 and later: +/* .IP "\fBvirtual_alias_address_length_limit (1000)\fR" +/* The maximal length of an email address after virtual alias expansion. +/* SMTPUTF8 CONTROLS +/* .ad +/* .fi +/* Preliminary SMTPUTF8 support is introduced with Postfix 3.0. +/* .IP "\fBsmtputf8_enable (yes)\fR" +/* Enable preliminary SMTPUTF8 support for the protocols described +/* in RFC 6531..6533. +/* .IP "\fBsmtputf8_autodetect_classes (sendmail, verify)\fR" +/* Detect that a message requires SMTPUTF8 support for the specified +/* mail origin classes. +/* .PP +/* Available in Postfix version 3.2 and later: +/* .IP "\fBenable_idna2003_compatibility (no)\fR" +/* Enable 'transitional' compatibility between IDNA2003 and IDNA2008, +/* when converting UTF-8 domain names to/from the ASCII form that is +/* used for DNS lookups. +/* 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 "\fBdelay_warning_time (0h)\fR" +/* The time after which the sender receives a copy of the message +/* headers of mail that is still queued. +/* .IP "\fBipc_timeout (3600s)\fR" +/* The time limit for sending or receiving information over an internal +/* communication channel. +/* .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 "\fBmyhostname (see 'postconf -d' output)\fR" +/* The internet hostname of this mail system. +/* .IP "\fBmyorigin ($myhostname)\fR" +/* The domain name that locally-posted mail appears to come +/* from, and that locally posted mail is 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 "\fBqueue_directory (see 'postconf -d' output)\fR" +/* The location of the Postfix top-level queue directory. +/* .IP "\fBsoft_bounce (no)\fR" +/* Safety net to keep mail queued that would otherwise be returned to +/* the sender. +/* .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 2.1 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). +/* .PP +/* Available in Postfix 3.3 and later: +/* .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 +/* /etc/postfix/canonical*, canonical mapping table +/* /etc/postfix/virtual*, virtual mapping table +/* SEE ALSO +/* trivial-rewrite(8), address rewriting +/* qmgr(8), queue manager +/* header_checks(5), message header content inspection +/* body_checks(5), body parts content inspection +/* canonical(5), canonical address lookup table format +/* virtual(5), virtual alias lookup table format +/* postconf(5), configuration parameters +/* master(5), generic daemon options +/* master(8), process manager +/* postlogd(8), Postfix logging +/* syslogd(8), system logging +/* README FILES +/* .ad +/* .fi +/* Use "\fBpostconf readme_directory\fR" or +/* "\fBpostconf html_directory\fR" to locate this information. +/* .na +/* .nf +/* ADDRESS_REWRITING_README Postfix address manipulation +/* CONTENT_INSPECTION_README content inspection +/* 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 <signal.h> +#include <unistd.h> +#include <stdlib.h> + +/* Utility library. */ + +#include <msg.h> +#include <vstring.h> +#include <dict.h> + +/* Global library. */ + +#include <mail_conf.h> +#include <cleanup_user.h> +#include <mail_proto.h> +#include <mail_params.h> +#include <record.h> +#include <rec_type.h> +#include <mail_version.h> + +/* Single-threaded server skeleton. */ + +#include <mail_server.h> + +/* Application-specific. */ + +#include "cleanup.h" + +/* cleanup_service - process one request to inject a message into the queue */ + +static void cleanup_service(VSTREAM *src, char *unused_service, char **argv) +{ + VSTRING *buf = vstring_alloc(100); + CLEANUP_STATE *state; + int flags; + int type = 0; + int status; + + /* + * Sanity check. This service takes no command-line arguments. + */ + if (argv[0]) + msg_fatal("unexpected command-line argument: %s", argv[0]); + + /* + * Open a queue file and initialize state. + */ + state = cleanup_open(src); + + /* + * Send the queue id to the client. Read client processing options. If we + * can't read the client processing options we can pretty much forget + * about the whole operation. + */ + attr_print(src, ATTR_FLAG_NONE, + SEND_ATTR_STR(MAIL_ATTR_QUEUEID, state->queue_id), + ATTR_TYPE_END); + if (attr_scan(src, ATTR_FLAG_STRICT, + RECV_ATTR_INT(MAIL_ATTR_FLAGS, &flags), + ATTR_TYPE_END) != 1) { + state->errs |= CLEANUP_STAT_BAD; + flags = 0; + } + cleanup_control(state, flags); + + /* + * XXX Rely on the front-end programs to enforce record size limits. + * + * First, copy the envelope records to the queue file. Then, copy the + * message content (headers and body). Finally, attach any information + * extracted from message headers. + */ + while (CLEANUP_OUT_OK(state)) { + if ((type = rec_get_raw(src, buf, 0, REC_FLAG_NONE)) < 0) { + state->errs |= CLEANUP_STAT_BAD; + break; + } + if (REC_GET_HIDDEN_TYPE(type)) { + msg_warn("%s: record type %d not allowed - discarding this message", + state->queue_id, type); + state->errs |= CLEANUP_STAT_BAD; + break; + } + CLEANUP_RECORD(state, type, vstring_str(buf), VSTRING_LEN(buf)); + if (type == REC_TYPE_END) + break; + } + + /* + * Keep reading in case of problems, until the sender is ready to receive + * our status report. + */ + if (CLEANUP_OUT_OK(state) == 0 && type > 0) { + while (type != REC_TYPE_END + && (type = rec_get_raw(src, buf, 0, REC_FLAG_NONE)) > 0) { + if (type == REC_TYPE_MILT_COUNT) { + int milter_count = atoi(vstring_str(buf)); + + /* Avoid deadlock. */ + if (milter_count >= 0) + cleanup_milter_receive(state, milter_count); + } + } + } + + /* + * Log something to make timeout errors easier to debug. + */ + if (vstream_ftimeout(src)) + msg_warn("%s: read timeout on %s", + state->queue_id, VSTREAM_PATH(src)); + + /* + * Finish this message, and report the result status to the client. + */ + status = cleanup_flush(state); /* in case state is modified */ + attr_print(src, ATTR_FLAG_NONE, + SEND_ATTR_INT(MAIL_ATTR_STATUS, status), + SEND_ATTR_STR(MAIL_ATTR_WHY, + (state->flags & CLEANUP_FLAG_SMTP_REPLY) + && state->smtp_reply ? state->smtp_reply : + state->reason ? state->reason : ""), + ATTR_TYPE_END); + cleanup_free(state); + + /* + * Cleanup. + */ + vstring_free(buf); +} + +/* 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); + } +} + +MAIL_VERSION_STAMP_DECLARE; + +/* main - the main program */ + +int main(int argc, char **argv) +{ + + /* + * Fingerprint executables and core dumps. + */ + MAIL_VERSION_STAMP_ALLOCATE; + + /* + * Clean up an incomplete queue file in case of a fatal run-time error, + * or after receiving SIGTERM from the master at shutdown time. + */ + signal(SIGTERM, cleanup_sig); + msg_cleanup(cleanup_all); + + /* + * Pass control to the single-threaded service skeleton. + */ + single_server_main(argc, argv, cleanup_service, + CA_MAIL_SERVER_INT_TABLE(cleanup_int_table), + CA_MAIL_SERVER_BOOL_TABLE(cleanup_bool_table), + CA_MAIL_SERVER_STR_TABLE(cleanup_str_table), + CA_MAIL_SERVER_TIME_TABLE(cleanup_time_table), + CA_MAIL_SERVER_PRE_INIT(cleanup_pre_jail), + CA_MAIL_SERVER_POST_INIT(cleanup_post_jail), + CA_MAIL_SERVER_PRE_ACCEPT(pre_accept), + CA_MAIL_SERVER_IN_FLOW_DELAY, + CA_MAIL_SERVER_UNLIMITED, + 0); +} diff --git a/src/cleanup/cleanup.h b/src/cleanup/cleanup.h new file mode 100644 index 0000000..df88a70 --- /dev/null +++ b/src/cleanup/cleanup.h @@ -0,0 +1,372 @@ +/*++ +/* NAME +/* cleanup 3h +/* SUMMARY +/* canonicalize and enqueue message +/* SYNOPSIS +/* #include "cleanup.h" +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include <sys/time.h> + + /* + * Utility library. + */ +#include <vstring.h> +#include <vstream.h> +#include <argv.h> +#include <nvtable.h> + + /* + * Global library. + */ +#include <maps.h> +#include <tok822.h> +#include <been_here.h> +#include <mail_stream.h> +#include <mail_conf.h> +#include <mime_state.h> +#include <string_list.h> +#include <cleanup_user.h> +#include <header_body_checks.h> +#include <dsn_mask.h> + + /* + * Milter library. + */ +#include <milter.h> + + /* + * These state variables are accessed by many functions, and there is only + * one instance of each per message. + */ +typedef struct CLEANUP_STATE { + VSTRING *attr_buf; /* storage for named attribute */ + VSTRING *temp1; /* scratch buffer, local use only */ + VSTRING *temp2; /* scratch buffer, local use only */ + VSTRING *stripped_buf; /* character stripped input */ + VSTREAM *src; /* current input stream */ + VSTREAM *dst; /* current output stream */ + MAIL_STREAM *handle; /* mail stream handle */ + char *queue_name; /* queue name */ + char *queue_id; /* queue file basename */ + struct timeval arrival_time; /* arrival time */ + char *fullname; /* envelope sender full name */ + char *sender; /* envelope sender address */ + char *recip; /* envelope recipient address */ + char *orig_rcpt; /* original recipient address */ + char *return_receipt; /* return-receipt address */ + char *errors_to; /* errors-to address */ + ARGV *auto_hdrs; /* MTA's own header(s) */ + ARGV *hbc_rcpt; /* header/body checks BCC addresses */ + int flags; /* processing options, status flags */ + int tflags; /* User- or MTA-requested tracing */ + int qmgr_opts; /* qmgr processing options */ + int errs; /* any badness experienced */ + int err_mask; /* allowed badness */ + int headers_seen; /* which headers were seen */ + int hop_count; /* count of received: headers */ + char *resent; /* any resent- header seen */ + BH_TABLE *dups; /* recipient dup filter */ + void (*action) (struct CLEANUP_STATE *, int, const char *, ssize_t); + off_t data_offset; /* start of message content */ + off_t body_offset; /* start of body content */ + off_t xtra_offset; /* start of extra segment */ + off_t cont_length; /* length including Milter edits */ + off_t sender_pt_offset; /* replace sender here */ + off_t sender_pt_target; /* record after sender address */ + off_t append_rcpt_pt_offset; /* append recipient here */ + off_t append_rcpt_pt_target; /* target of above record */ + off_t append_hdr_pt_offset; /* append header here */ + off_t append_hdr_pt_target; /* target of above record */ + off_t append_meta_pt_offset; /* append meta record here */ + off_t append_meta_pt_target; /* target of above record */ + ssize_t rcpt_count; /* recipient count */ + char *reason; /* failure reason */ + char *smtp_reply; /* failure reason, SMTP-style */ + NVTABLE *attr; /* queue file attribute list */ + MIME_STATE *mime_state; /* MIME state engine */ + int mime_errs; /* MIME error flags */ + char *hdr_rewrite_context; /* header rewrite context */ + char *filter; /* from header/body patterns */ + char *redirect; /* from header/body patterns */ + char *dsn_envid; /* DSN envelope ID */ + int dsn_ret; /* DSN full/hdrs */ + int dsn_notify; /* DSN never/delay/fail/success */ + char *dsn_orcpt; /* DSN original recipient */ + char *verp_delims; /* VERP delimiters (optional) */ +#ifdef DELAY_ACTION + int defer_delay; /* deferred delivery */ +#endif + + /* + * Miscellaneous Milter support. + */ + MILTERS *milters; /* mail filters */ + const char *client_name; /* real or ersatz client */ + const char *reverse_name; /* real or ersatz client */ + const char *client_addr; /* real or ersatz client */ + int client_af; /* real or ersatz client */ + const char *client_port; /* real or ersatz client */ + const char *server_addr; /* real or ersatz server */ + const char *server_port; /* real or ersatz server */ + VSTRING *milter_ext_from; /* externalized sender */ + VSTRING *milter_ext_rcpt; /* externalized recipient */ + VSTRING *milter_err_text; /* milter call-back reply */ + VSTRING *milter_dsn_buf; /* Milter DSN parsing buffer */ + + /* + * Support for Milter body replacement requests. + */ + struct CLEANUP_REGION *free_regions;/* unused regions */ + struct CLEANUP_REGION *body_regions;/* regions with body content */ + struct CLEANUP_REGION *curr_body_region; + + /* + * Internationalization. + */ + int smtputf8; /* what support is desired */ +} CLEANUP_STATE; + + /* + * Status flags. Flags 0-15 are reserved for cleanup_user.h. + */ +#define CLEANUP_FLAG_INRCPT (1<<16) /* Processing recipient records */ +#define CLEANUP_FLAG_WARN_SEEN (1<<17) /* REC_TYPE_WARN record seen */ +#define CLEANUP_FLAG_END_SEEN (1<<18) /* REC_TYPE_END record seen */ + + /* + * Mappings. + */ +extern MAPS *cleanup_comm_canon_maps; +extern MAPS *cleanup_send_canon_maps; +extern MAPS *cleanup_rcpt_canon_maps; +extern int cleanup_comm_canon_flags; +extern int cleanup_send_canon_flags; +extern int cleanup_rcpt_canon_flags; +extern MAPS *cleanup_header_checks; +extern MAPS *cleanup_mimehdr_checks; +extern MAPS *cleanup_nesthdr_checks; +extern MAPS *cleanup_body_checks; +extern MAPS *cleanup_virt_alias_maps; +extern ARGV *cleanup_masq_domains; +extern STRING_LIST *cleanup_masq_exceptions; +extern int cleanup_masq_flags; +extern MAPS *cleanup_send_bcc_maps; +extern MAPS *cleanup_rcpt_bcc_maps; + + /* + * Character filters. + */ +extern VSTRING *cleanup_reject_chars; +extern VSTRING *cleanup_strip_chars; + + /* + * Milters. + */ +extern MILTERS *cleanup_milters; + + /* + * Address canonicalization fine control. + */ +#define CLEANUP_CANON_FLAG_ENV_FROM (1<<0) /* envelope sender */ +#define CLEANUP_CANON_FLAG_ENV_RCPT (1<<1) /* envelope recipient */ +#define CLEANUP_CANON_FLAG_HDR_FROM (1<<2) /* header sender */ +#define CLEANUP_CANON_FLAG_HDR_RCPT (1<<3) /* header recipient */ + + /* + * Address masquerading fine control. + */ +#define CLEANUP_MASQ_FLAG_ENV_FROM (1<<0) /* envelope sender */ +#define CLEANUP_MASQ_FLAG_ENV_RCPT (1<<1) /* envelope recipient */ +#define CLEANUP_MASQ_FLAG_HDR_FROM (1<<2) /* header sender */ +#define CLEANUP_MASQ_FLAG_HDR_RCPT (1<<3) /* header recipient */ + + /* + * Restrictions on extension propagation. + */ +extern int cleanup_ext_prop_mask; + + /* + * Saved queue file names, so the files can be removed in case of a fatal + * run-time error. + */ +extern char *cleanup_path; +extern VSTRING *cleanup_trace_path; +extern VSTRING *cleanup_bounce_path; + + /* + * cleanup_state.c + */ +extern CLEANUP_STATE *cleanup_state_alloc(VSTREAM *); +extern void cleanup_state_free(CLEANUP_STATE *); + + /* + * cleanup_api.c + */ +extern CLEANUP_STATE *cleanup_open(VSTREAM *); +extern void cleanup_control(CLEANUP_STATE *, int); +extern int cleanup_flush(CLEANUP_STATE *); +extern void cleanup_free(CLEANUP_STATE *); +extern void cleanup_all(void); +extern void cleanup_sig(int); +extern void cleanup_pre_jail(char *, char **); +extern void cleanup_post_jail(char *, char **); +extern const CONFIG_INT_TABLE cleanup_int_table[]; +extern const CONFIG_BOOL_TABLE cleanup_bool_table[]; +extern const CONFIG_STR_TABLE cleanup_str_table[]; +extern const CONFIG_TIME_TABLE cleanup_time_table[]; + +#define CLEANUP_RECORD(s, t, b, l) ((s)->action((s), (t), (b), (l))) + + /* + * cleanup_out.c + */ +extern void cleanup_out(CLEANUP_STATE *, int, const char *, ssize_t); +extern void cleanup_out_string(CLEANUP_STATE *, int, const char *); +extern void PRINTFLIKE(3, 4) cleanup_out_format(CLEANUP_STATE *, int, const char *,...); +extern void cleanup_out_header(CLEANUP_STATE *, VSTRING *); + +#define CLEANUP_OUT_BUF(s, t, b) \ + cleanup_out((s), (t), vstring_str((b)), VSTRING_LEN((b))) + +#define CLEANUP_OUT_OK(s) \ + (!((s)->errs & (s)->err_mask) && !((s)->flags & CLEANUP_FLAG_DISCARD)) + + /* + * cleanup_envelope.c + */ +extern void cleanup_envelope(CLEANUP_STATE *, int, const char *, ssize_t); + + /* + * cleanup_message.c + */ +extern void cleanup_message(CLEANUP_STATE *, int, const char *, ssize_t); + + /* + * cleanup_extracted.c + */ +extern void cleanup_extracted(CLEANUP_STATE *, int, const char *, ssize_t); + + /* + * cleanup_final.c + */ +extern void cleanup_final(CLEANUP_STATE *); + + /* + * cleanup_rewrite.c + */ +extern int cleanup_rewrite_external(const char *, VSTRING *, const char *); +extern int cleanup_rewrite_internal(const char *, VSTRING *, const char *); +extern int cleanup_rewrite_tree(const char *, TOK822 *); + + /* + * cleanup_map11.c + */ +extern int cleanup_map11_external(CLEANUP_STATE *, VSTRING *, MAPS *, int); +extern int cleanup_map11_internal(CLEANUP_STATE *, VSTRING *, MAPS *, int); +extern int cleanup_map11_tree(CLEANUP_STATE *, TOK822 *, MAPS *, int); + + /* + * cleanup_map1n.c + */ +ARGV *cleanup_map1n_internal(CLEANUP_STATE *, const char *, MAPS *, int); + + /* + * cleanup_masquerade.c + */ +extern int cleanup_masquerade_external(CLEANUP_STATE *, VSTRING *, ARGV *); +extern int cleanup_masquerade_internal(CLEANUP_STATE *, VSTRING *, ARGV *); +extern int cleanup_masquerade_tree(CLEANUP_STATE *, TOK822 *, ARGV *); + + /* + * cleanup_recipient.c + */ +extern void cleanup_out_recipient(CLEANUP_STATE *, const char *, int, const char *, const char *); + + /* + * cleanup_addr.c. + */ +extern off_t cleanup_addr_sender(CLEANUP_STATE *, const char *); +extern void cleanup_addr_recipient(CLEANUP_STATE *, const char *); +extern void cleanup_addr_bcc_dsn(CLEANUP_STATE *, const char *, const char *, int); + +#define NO_DSN_ORCPT ((char *) 0) +#define NO_DSN_NOTIFY DSN_NOTIFY_NEVER +#define DEF_DSN_NOTIFY (0) + +#define cleanup_addr_bcc(state, addr) \ + cleanup_addr_bcc_dsn((state), (addr), NO_DSN_ORCPT, NO_DSN_NOTIFY) + + /* + * cleanup_bounce.c. + */ +extern int cleanup_bounce(CLEANUP_STATE *); + + /* + * MSG_STATS compatibility. + */ +#define CLEANUP_MSG_STATS(stats, state) \ + MSG_STATS_INIT1(stats, incoming_arrival, state->arrival_time) + + /* + * cleanup_milter.c. + */ +extern void cleanup_milter_header_checks_init(void); +extern void cleanup_milter_receive(CLEANUP_STATE *, int); +extern void cleanup_milter_inspect(CLEANUP_STATE *, MILTERS *); +extern void cleanup_milter_emul_mail(CLEANUP_STATE *, MILTERS *, const char *); +extern void cleanup_milter_emul_rcpt(CLEANUP_STATE *, MILTERS *, const char *); +extern void cleanup_milter_emul_data(CLEANUP_STATE *, MILTERS *); + +#define CLEANUP_MILTER_OK(s) \ + (((s)->flags & CLEANUP_FLAG_MILTER) != 0 \ + && (s)->errs == 0 && ((s)->flags & CLEANUP_FLAG_DISCARD) == 0) + + /* + * cleanup_body_edit.c + */ +typedef struct CLEANUP_REGION { + off_t start; /* start of region */ + off_t len; /* length or zero (open-ended) */ + off_t write_offs; /* write offset */ + struct CLEANUP_REGION *next; /* linkage */ +} CLEANUP_REGION; + +extern void cleanup_region_init(CLEANUP_STATE *); +extern CLEANUP_REGION *cleanup_region_open(CLEANUP_STATE *, ssize_t); +extern void cleanup_region_close(CLEANUP_STATE *, CLEANUP_REGION *); +extern CLEANUP_REGION *cleanup_region_return(CLEANUP_STATE *, CLEANUP_REGION *); +extern void cleanup_region_done(CLEANUP_STATE *); + +extern int cleanup_body_edit_start(CLEANUP_STATE *); +extern int cleanup_body_edit_write(CLEANUP_STATE *, int, VSTRING *); +extern int cleanup_body_edit_finish(CLEANUP_STATE *); +extern void cleanup_body_edit_free(CLEANUP_STATE *); + + /* + * From: header formatting. + */ +#define HFROM_FORMAT_CODE_STD 0 +#define HFROM_FORMAT_CODE_OBS 1 +extern int hfrom_format_code; + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ diff --git a/src/cleanup/cleanup_addr.c b/src/cleanup/cleanup_addr.c new file mode 100644 index 0000000..fd8a511 --- /dev/null +++ b/src/cleanup/cleanup_addr.c @@ -0,0 +1,286 @@ +/*++ +/* NAME +/* cleanup_addr 3 +/* SUMMARY +/* process envelope addresses +/* SYNOPSIS +/* #include <cleanup.h> +/* +/* off_t cleanup_addr_sender(state, addr) +/* CLEANUP_STATE *state; +/* const char *addr; +/* +/* void cleanup_addr_recipient(state, addr) +/* CLEANUP_STATE *state; +/* const char *addr; +/* +/* void cleanup_addr_bcc_dsn(state, addr, dsn_orcpt, dsn_notify) +/* CLEANUP_STATE *state; +/* const char *addr; +/* const char *dsn_orcpt; +/* int dsn_notify; +/* +/* void cleanup_addr_bcc(state, addr) +/* CLEANUP_STATE *state; +/* const char *addr; +/* DESCRIPTION +/* This module processes envelope address records and writes the result +/* to the queue file. Processing includes address rewriting and +/* sender/recipient auto bcc address generation. +/* +/* cleanup_addr_sender() processes sender envelope information and updates +/* state->sender. The result value is the offset of the record that +/* follows the sender record if milters are enabled, otherwise zero. +/* +/* cleanup_addr_recipient() processes recipient envelope information +/* and updates state->recip. +/* +/* cleanup_addr_bcc_dsn() processes recipient envelope information. This +/* is a separate function to avoid invoking cleanup_addr_recipient() +/* recursively. +/* +/* cleanup_addr_bcc() is a backwards-compatibility wrapper for +/* cleanup_addr_bcc_dsn() that requests no delivery status +/* notification for the recipient. +/* +/* Arguments: +/* .IP state +/* Queue file and message processing state. This state is updated +/* as records are processed and as errors happen. +/* .IP buf +/* Record content. +/* .IP dsn_orcpt +/* The DSN original recipient (or NO_DSN_ORCPT to specify none). +/* .IP dsn_notify +/* DSN notification options. Specify NO_DSN_NOTIFY to disable +/* notification, and DEF_DSN_NOTIFY for default notification. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <string.h> +#include <stdlib.h> + +/* Utility library. */ + +#include <msg.h> +#include <vstring.h> +#include <vstream.h> +#include <mymalloc.h> +#include <stringops.h> + +/* Global library. */ + +#include <rec_type.h> +#include <record.h> +#include <cleanup_user.h> +#include <mail_params.h> +#include <ext_prop.h> +#include <mail_addr.h> +#include <canon_addr.h> +#include <mail_addr_find.h> +#include <mail_proto.h> +#include <dsn_mask.h> +#include <smtputf8.h> + +/* Application-specific. */ + +#include "cleanup.h" + +#define STR vstring_str +#define LEN VSTRING_LEN +#define IGNORE_EXTENSION (char **) 0 + +/* cleanup_addr_sender - process envelope sender record */ + +off_t cleanup_addr_sender(CLEANUP_STATE *state, const char *buf) +{ + const char myname[] = "cleanup_addr_sender"; + VSTRING *clean_addr = vstring_alloc(100); + off_t after_sender_offs = 0; + const char *bcc; + size_t len; + + /* + * Note: an unqualified envelope address is for all practical purposes + * equivalent to a fully qualified local address, both for delivery and + * for replying. Having to support both forms is error prone, therefore + * an incomplete envelope address is rewritten to fully qualified form in + * the local domain context. + * + * 20000520: Replace mailer-daemon@$myorigin by the null address, to handle + * bounced mail traffic more robustly. + */ + cleanup_rewrite_internal(MAIL_ATTR_RWR_LOCAL, clean_addr, buf); + if (strncasecmp_utf8(STR(clean_addr), MAIL_ADDR_MAIL_DAEMON "@", + sizeof(MAIL_ADDR_MAIL_DAEMON)) == 0) { + canon_addr_internal(state->temp1, MAIL_ADDR_MAIL_DAEMON); + if (strcasecmp_utf8(STR(clean_addr), STR(state->temp1)) == 0) + vstring_strcpy(clean_addr, ""); + } + if (state->flags & CLEANUP_FLAG_MAP_OK) { + if (cleanup_send_canon_maps + && (cleanup_send_canon_flags & CLEANUP_CANON_FLAG_ENV_FROM)) + cleanup_map11_internal(state, clean_addr, cleanup_send_canon_maps, + cleanup_ext_prop_mask & EXT_PROP_CANONICAL); + if (cleanup_comm_canon_maps + && (cleanup_comm_canon_flags & CLEANUP_CANON_FLAG_ENV_FROM)) + cleanup_map11_internal(state, clean_addr, cleanup_comm_canon_maps, + cleanup_ext_prop_mask & EXT_PROP_CANONICAL); + if (cleanup_masq_domains + && (cleanup_masq_flags & CLEANUP_MASQ_FLAG_ENV_FROM)) + cleanup_masquerade_internal(state, clean_addr, cleanup_masq_domains); + } + /* Fix 20140711: Auto-detect an UTF8 sender. */ + if (var_smtputf8_enable && *STR(clean_addr) && !allascii(STR(clean_addr)) + && valid_utf8_string(STR(clean_addr), LEN(clean_addr))) { + state->smtputf8 |= SMTPUTF8_FLAG_SENDER; + /* Fix 20140713: request SMTPUTF8 support selectively. */ + if (state->flags & CLEANUP_FLAG_AUTOUTF8) + state->smtputf8 |= SMTPUTF8_FLAG_REQUESTED; + } + CLEANUP_OUT_BUF(state, REC_TYPE_FROM, clean_addr); + if (state->sender) /* XXX Can't happen */ + myfree(state->sender); + state->sender = mystrdup(STR(clean_addr)); /* Used by Milter client */ + /* Fix 20160310: Moved from cleanup_envelope.c. */ + if (state->milters || cleanup_milters) { + /* Make room to replace sender. */ + if ((len = LEN(clean_addr)) < REC_TYPE_PTR_PAYL_SIZE) + rec_pad(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_PAYL_SIZE - len); + /* Remember the after-sender record offset. */ + if ((after_sender_offs = vstream_ftell(state->dst)) < 0) + msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); + } + if ((state->flags & CLEANUP_FLAG_BCC_OK) + && *STR(clean_addr) + && cleanup_send_bcc_maps) { + if ((bcc = mail_addr_find_to_internal(cleanup_send_bcc_maps, + STR(clean_addr), + IGNORE_EXTENSION)) != 0) { + cleanup_addr_bcc(state, bcc); + } else if (cleanup_send_bcc_maps->error) { + msg_warn("%s: %s map lookup problem -- " + "message not accepted, try again later", + state->queue_id, cleanup_send_bcc_maps->title); + state->errs |= CLEANUP_STAT_WRITE; + } + } + vstring_free(clean_addr); + return after_sender_offs; +} + +/* cleanup_addr_recipient - process envelope recipient */ + +void cleanup_addr_recipient(CLEANUP_STATE *state, const char *buf) +{ + VSTRING *clean_addr = vstring_alloc(100); + const char *bcc; + + /* + * Note: an unqualified envelope address is for all practical purposes + * equivalent to a fully qualified local address, both for delivery and + * for replying. Having to support both forms is error prone, therefore + * an incomplete envelope address is rewritten to fully qualified form in + * the local domain context. + */ + cleanup_rewrite_internal(MAIL_ATTR_RWR_LOCAL, + clean_addr, *buf ? buf : var_empty_addr); + if (state->flags & CLEANUP_FLAG_MAP_OK) { + if (cleanup_rcpt_canon_maps + && (cleanup_rcpt_canon_flags & CLEANUP_CANON_FLAG_ENV_RCPT)) + cleanup_map11_internal(state, clean_addr, cleanup_rcpt_canon_maps, + cleanup_ext_prop_mask & EXT_PROP_CANONICAL); + if (cleanup_comm_canon_maps + && (cleanup_comm_canon_flags & CLEANUP_CANON_FLAG_ENV_RCPT)) + cleanup_map11_internal(state, clean_addr, cleanup_comm_canon_maps, + cleanup_ext_prop_mask & EXT_PROP_CANONICAL); + if (cleanup_masq_domains + && (cleanup_masq_flags & CLEANUP_MASQ_FLAG_ENV_RCPT)) + cleanup_masquerade_internal(state, clean_addr, cleanup_masq_domains); + } + /* Fix 20140711: Auto-detect an UTF8 recipient. */ + if (var_smtputf8_enable && *STR(clean_addr) && !allascii(STR(clean_addr)) + && valid_utf8_string(STR(clean_addr), LEN(clean_addr))) { + /* Fix 20140713: request SMTPUTF8 support selectively. */ + if (state->flags & CLEANUP_FLAG_AUTOUTF8) + state->smtputf8 |= SMTPUTF8_FLAG_REQUESTED; + } + /* Fix 20141024: Don't fake up a "bare" DSN original rcpt in smtp(8). */ + if (state->dsn_orcpt == 0 && *STR(clean_addr) != 0) + state->dsn_orcpt = concatenate((!allascii(STR(clean_addr)) + && (state->smtputf8 & SMTPUTF8_FLAG_REQUESTED)) ? + "utf-8" : "rfc822", ";", STR(clean_addr), (char *) 0); + cleanup_out_recipient(state, state->dsn_orcpt, state->dsn_notify, + state->orig_rcpt, STR(clean_addr)); + if (state->recip) /* This can happen */ + myfree(state->recip); + state->recip = mystrdup(STR(clean_addr)); /* Used by Milter client */ + if ((state->flags & CLEANUP_FLAG_BCC_OK) + && *STR(clean_addr) + && cleanup_rcpt_bcc_maps) { + if ((bcc = mail_addr_find_to_internal(cleanup_rcpt_bcc_maps, + STR(clean_addr), + IGNORE_EXTENSION)) != 0) { + cleanup_addr_bcc(state, bcc); + } else if (cleanup_rcpt_bcc_maps->error) { + msg_warn("%s: %s map lookup problem -- " + "message not accepted, try again later", + state->queue_id, cleanup_rcpt_bcc_maps->title); + state->errs |= CLEANUP_STAT_WRITE; + } + } + vstring_free(clean_addr); +} + +/* cleanup_addr_bcc_dsn - process automatic BCC recipient */ + +void cleanup_addr_bcc_dsn(CLEANUP_STATE *state, const char *bcc, + const char *dsn_orcpt, int dsn_notify) +{ + VSTRING *clean_addr = vstring_alloc(100); + + /* + * Note: BCC addresses are supplied locally, and must be rewritten in the + * local address rewriting context. + */ + cleanup_rewrite_internal(MAIL_ATTR_RWR_LOCAL, clean_addr, bcc); + if (state->flags & CLEANUP_FLAG_MAP_OK) { + if (cleanup_rcpt_canon_maps + && (cleanup_rcpt_canon_flags & CLEANUP_CANON_FLAG_ENV_RCPT)) + cleanup_map11_internal(state, clean_addr, cleanup_rcpt_canon_maps, + cleanup_ext_prop_mask & EXT_PROP_CANONICAL); + if (cleanup_comm_canon_maps + && (cleanup_comm_canon_flags & CLEANUP_CANON_FLAG_ENV_RCPT)) + cleanup_map11_internal(state, clean_addr, cleanup_comm_canon_maps, + cleanup_ext_prop_mask & EXT_PROP_CANONICAL); + if (cleanup_masq_domains + && (cleanup_masq_flags & CLEANUP_MASQ_FLAG_ENV_RCPT)) + cleanup_masquerade_internal(state, clean_addr, cleanup_masq_domains); + } + /* Fix 20140711: Auto-detect an UTF8 recipient. */ + if (var_smtputf8_enable && *STR(clean_addr) && !allascii(STR(clean_addr)) + && valid_utf8_string(STR(clean_addr), LEN(clean_addr))) { + /* Fix 20140713: request SMTPUTF8 support selectively. */ + if (state->flags & CLEANUP_FLAG_AUTOUTF8) + state->smtputf8 |= SMTPUTF8_FLAG_REQUESTED; + } + cleanup_out_recipient(state, dsn_orcpt, dsn_notify, + STR(clean_addr), STR(clean_addr)); + vstring_free(clean_addr); +} diff --git a/src/cleanup/cleanup_api.c b/src/cleanup/cleanup_api.c new file mode 100644 index 0000000..4fc5e2e --- /dev/null +++ b/src/cleanup/cleanup_api.c @@ -0,0 +1,395 @@ +/*++ +/* NAME +/* cleanup_api 3 +/* SUMMARY +/* cleanup callable interface, message processing +/* SYNOPSIS +/* #include "cleanup.h" +/* +/* CLEANUP_STATE *cleanup_open(src) +/* VSTREAM *src; +/* +/* void cleanup_control(state, flags) +/* CLEANUP_STATE *state; +/* int flags; +/* +/* void CLEANUP_RECORD(state, type, buf, len) +/* CLEANUP_STATE *state; +/* int type; +/* char *buf; +/* int len; +/* +/* int cleanup_flush(state) +/* CLEANUP_STATE *state; +/* +/* int cleanup_free(state) +/* CLEANUP_STATE *state; +/* DESCRIPTION +/* This module implements a callable interface to the cleanup service +/* for processing one message and for writing it to queue file. +/* For a description of the cleanup service, see cleanup(8). +/* +/* cleanup_open() creates a new queue file and performs other +/* per-message initialization. The result is a handle that should be +/* given to the cleanup_control(), cleanup_record(), cleanup_flush() +/* and cleanup_free() routines. The name of the queue file is in the +/* queue_id result structure member. +/* +/* cleanup_control() processes per-message flags specified by the caller. +/* These flags control the handling of data errors, and must be set +/* before processing the first message record. +/* .IP CLEANUP_FLAG_BOUNCE +/* The cleanup server is responsible for returning undeliverable +/* mail (too many hops, message too large) to the sender. +/* .IP CLEANUP_FLAG_BCC_OK +/* It is OK to add automatic BCC recipient addresses. +/* .IP CLEANUP_FLAG_FILTER +/* Enable header/body filtering. This should be enabled only with mail +/* that enters Postfix, not with locally forwarded mail or with bounce +/* messages. +/* .IP CLEANUP_FLAG_MILTER +/* Enable Milter applications. This should be enabled only with mail +/* that enters Postfix, not with locally forwarded mail or with bounce +/* messages. +/* .IP CLEANUP_FLAG_MAP_OK +/* Enable canonical and virtual mapping, and address masquerading. +/* .PP +/* For convenience the CLEANUP_FLAG_MASK_EXTERNAL macro specifies +/* the options that are normally needed for mail that enters +/* Postfix from outside, and CLEANUP_FLAG_MASK_INTERNAL specifies +/* the options that are normally needed for internally generated or +/* forwarded mail. +/* +/* CLEANUP_RECORD() is a macro that processes one message record, +/* that copies the result to the queue file, and that maintains a +/* little state machine. The last record in a valid message has type +/* REC_TYPE_END. In order to find out if a message is corrupted, +/* the caller is encouraged to test the CLEANUP_OUT_OK(state) macro. +/* The result is false when further message processing is futile. +/* In that case, it is safe to call cleanup_flush() immediately. +/* +/* cleanup_flush() closes a queue file. In case of any errors, +/* the file is removed. The result value is non-zero in case of +/* problems. In some cases a human-readable text can be found in +/* the state->reason member. In all other cases, use cleanup_strerror() +/* to translate the result into human-readable text. +/* +/* cleanup_free() destroys its argument. +/* .IP CLEANUP_FLAG_SMTPUTF8 +/* Request SMTPUTF8 support when delivering mail. +/* .IP CLEANUP_FLAG_AUTOUTF8 +/* Autodetection: request SMTPUTF8 support if the message +/* contains an UTF8 message header, sender, or recipient. +/* DIAGNOSTICS +/* Problems and transactions are logged to \fBsyslogd\fR(8) +/* or \fBpostlogd\fR(8). +/* SEE ALSO +/* cleanup(8) cleanup service description. +/* cleanup_init(8) cleanup callable interface, initialization +/* 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 <errno.h> + +/* Utility library. */ + +#include <msg.h> +#include <vstring.h> +#include <mymalloc.h> + +/* Global library. */ + +#include <cleanup_user.h> +#include <mail_queue.h> +#include <mail_proto.h> +#include <bounce.h> +#include <mail_params.h> +#include <mail_stream.h> +#include <mail_flow.h> +#include <rec_type.h> +#include <smtputf8.h> + +/* Milter library. */ + +#include <milter.h> + +/* Application-specific. */ + +#include "cleanup.h" + +/* cleanup_open - open queue file and initialize */ + +CLEANUP_STATE *cleanup_open(VSTREAM *src) +{ + CLEANUP_STATE *state; + static const char *log_queues[] = { + MAIL_QUEUE_DEFER, + MAIL_QUEUE_BOUNCE, + MAIL_QUEUE_TRACE, + 0, + }; + const char **cpp; + + /* + * Initialize private state. + */ + state = cleanup_state_alloc(src); + + /* + * Open the queue file. Save the queue file name in a global variable, so + * that the runtime error handler can clean up in case of problems. + * + * XXX For now, a lot of detail is frozen that could be more useful if it + * were made configurable. + */ + state->queue_name = mystrdup(MAIL_QUEUE_INCOMING); + state->handle = mail_stream_file(state->queue_name, + MAIL_CLASS_PUBLIC, var_queue_service, 0); + state->dst = state->handle->stream; + cleanup_path = mystrdup(VSTREAM_PATH(state->dst)); + state->queue_id = mystrdup(state->handle->id); + if (msg_verbose) + msg_info("cleanup_open: open %s", cleanup_path); + + /* + * If there is a time to get rid of spurious log files, this is it. The + * down side is that this costs performance for every message, while the + * probability of spurious log files is quite low. + * + * XXX The defer logfile is deleted when the message is moved into the + * active queue. We must also remove it now, otherwise mailq produces + * nonsense. + */ + for (cpp = log_queues; *cpp; cpp++) { + if (mail_queue_remove(*cpp, state->queue_id) == 0) + msg_warn("%s: removed spurious %s log", *cpp, state->queue_id); + else if (errno != ENOENT) + msg_fatal("%s: remove %s log: %m", *cpp, state->queue_id); + } + return (state); +} + +/* cleanup_control - process client options */ + +void cleanup_control(CLEANUP_STATE *state, int flags) +{ + + /* + * If the client requests us to do the bouncing in case of problems, + * throw away the input only in case of real show-stopper errors, such as + * unrecognizable data (which should never happen) or insufficient space + * for the queue file (which will happen occasionally). Otherwise, + * discard input after any lethal error. See the CLEANUP_OUT_OK() macro + * definition. + */ + if (msg_verbose) + msg_info("cleanup flags = %s", cleanup_strflags(flags)); + if ((state->flags = flags) & CLEANUP_FLAG_BOUNCE) { + state->err_mask = CLEANUP_STAT_MASK_INCOMPLETE; + } else { + state->err_mask = ~0; + } + if (state->flags & CLEANUP_FLAG_SMTPUTF8) + state->smtputf8 = SMTPUTF8_FLAG_REQUESTED; +} + +/* cleanup_flush - finish queue file */ + +int cleanup_flush(CLEANUP_STATE *state) +{ + int status; + char *junk; + VSTRING *trace_junk; + + /* + * Raise these errors only if we examined all queue file records. + */ + if (CLEANUP_OUT_OK(state)) { + if (state->recip == 0) + state->errs |= CLEANUP_STAT_RCPT; + if ((state->flags & CLEANUP_FLAG_END_SEEN) == 0) + state->errs |= CLEANUP_STAT_BAD; + } + + /* + * Status sanitization. Always report success when the discard flag was + * raised by some user-specified access rule. + */ + if (state->flags & CLEANUP_FLAG_DISCARD) + state->errs = 0; + + /* + * Apply external mail filter. + * + * XXX Include test for a built-in action to tempfail this message. + */ + if (CLEANUP_MILTER_OK(state)) { + if (state->milters) + cleanup_milter_inspect(state, state->milters); + else if (cleanup_milters) { + cleanup_milter_emul_data(state, cleanup_milters); + if (CLEANUP_MILTER_OK(state)) + cleanup_milter_inspect(state, cleanup_milters); + } + } + + /* + * Update the preliminary message size and count fields with the actual + * values. + */ + if (CLEANUP_OUT_OK(state)) + cleanup_final(state); + + /* + * If there was an error that requires us to generate a bounce message + * (mail submitted with the Postfix sendmail command, mail forwarded by + * the local(8) delivery agent, or mail re-queued with "postsuper -r"), + * send a bounce notification, reset the error flags in case of success, + * and request deletion of the the incoming queue file and of the + * optional DSN SUCCESS records from virtual alias expansion. + * + * XXX It would make no sense to knowingly report success after we already + * have bounced all recipients, especially because the information in the + * DSN SUCCESS notice is completely redundant compared to the information + * in the bounce notice (however, both may be incomplete when the queue + * file size would exceed the safety limit). + * + * An alternative is to keep the DSN SUCCESS records and to delegate bounce + * notification to the queue manager, just like we already delegate + * success notification. This requires that we leave the undeliverable + * message in the incoming queue; versions up to 20050726 did exactly + * that. Unfortunately, this broke with over-size queue files, because + * the queue manager cannot handle incomplete queue files (and it should + * not try to do so). + */ +#define CAN_BOUNCE() \ + ((state->errs & CLEANUP_STAT_MASK_CANT_BOUNCE) == 0 \ + && state->sender != 0 \ + && (state->flags & CLEANUP_FLAG_BOUNCE) != 0) + + if (state->errs != 0 && CAN_BOUNCE()) + cleanup_bounce(state); + + /* + * Optionally, place the message on hold, but only if the message was + * received successfully and only if it's not being discarded for other + * reasons. This involves renaming the queue file before "finishing" it + * (or else the queue manager would grab it too early) and updating our + * own idea of the queue file name for error recovery and for error + * reporting purposes. + * + * XXX Include test for a built-in action to tempfail this message. + */ + if (state->errs == 0 && (state->flags & CLEANUP_FLAG_DISCARD) == 0) { + if ((state->flags & CLEANUP_FLAG_HOLD) != 0 +#ifdef DELAY_ACTION + || state->defer_delay > 0 +#endif + ) { + myfree(state->queue_name); +#ifdef DELAY_ACTION + state->queue_name = mystrdup((state->flags & CLEANUP_FLAG_HOLD) ? + MAIL_QUEUE_HOLD : MAIL_QUEUE_DEFERRED); +#else + state->queue_name = mystrdup(MAIL_QUEUE_HOLD); +#endif + mail_stream_ctl(state->handle, + CA_MAIL_STREAM_CTL_QUEUE(state->queue_name), + CA_MAIL_STREAM_CTL_CLASS((char *) 0), + CA_MAIL_STREAM_CTL_SERVICE((char *) 0), +#ifdef DELAY_ACTION + CA_MAIL_STREAM_CTL_DELAY(state->defer_delay), +#endif + CA_MAIL_STREAM_CTL_END); + junk = cleanup_path; + cleanup_path = mystrdup(VSTREAM_PATH(state->handle->stream)); + myfree(junk); + + /* + * XXX: When delivering to a non-incoming queue, do not consume + * in_flow tokens. Unfortunately we can't move the code that + * consumes tokens until after the mail is received, because that + * would increase the risk of duplicate deliveries (RFC 1047). + */ + (void) mail_flow_put(1); + } + state->errs = mail_stream_finish(state->handle, (VSTRING *) 0); + } else { + + /* + * XXX: When discarding mail, should we consume in_flow tokens? See + * also the comments above for mail that is placed on hold. + */ +#if 0 + (void) mail_flow_put(1); +#endif + mail_stream_cleanup(state->handle); + } + state->handle = 0; + state->dst = 0; + + /* + * If there was an error, or if the message must be discarded for other + * reasons, remove the queue file and the optional trace file with DSN + * SUCCESS records from virtual alias expansion. + */ + if (state->errs != 0 || (state->flags & CLEANUP_FLAG_DISCARD) != 0) { + if (cleanup_trace_path) + (void) REMOVE(vstring_str(cleanup_trace_path)); + if (REMOVE(cleanup_path)) + msg_warn("remove %s: %m", cleanup_path); + } + + /* + * Make sure that our queue file will not be deleted by the error handler + * AFTER we have taken responsibility for delivery. Better to deliver + * twice than to lose mail. + */ + trace_junk = cleanup_trace_path; + cleanup_trace_path = 0; /* don't delete upon error */ + junk = cleanup_path; + cleanup_path = 0; /* don't delete upon error */ + + if (trace_junk) + vstring_free(trace_junk); + myfree(junk); + + /* + * Cleanup internal state. This is simply complementary to the + * initializations at the beginning of cleanup_open(). + */ + if (msg_verbose) + msg_info("cleanup_flush: status %d", state->errs); + status = state->errs; + return (status); +} + +/* cleanup_free - pay the last respects */ + +void cleanup_free(CLEANUP_STATE *state) +{ + + /* + * Emulate disconnect event. CLEANUP_FLAG_MILTER may be turned off after + * we have started. + */ + if (cleanup_milters != 0 && state->milters == 0) + milter_disc_event(cleanup_milters); + cleanup_state_free(state); +} diff --git a/src/cleanup/cleanup_body_edit.c b/src/cleanup/cleanup_body_edit.c new file mode 100644 index 0000000..2fff1bc --- /dev/null +++ b/src/cleanup/cleanup_body_edit.c @@ -0,0 +1,236 @@ +/*++ +/* NAME +/* cleanup_body_edit 3 +/* SUMMARY +/* edit body content +/* SYNOPSIS +/* #include "cleanup.h" +/* +/* int cleanup_body_edit_start(state) +/* CLEANUP_STATE *state; +/* +/* int cleanup_body_edit_write(state, type, buf) +/* CLEANUP_STATE *state; +/* int type; +/* VSTRING *buf; +/* +/* int cleanup_body_edit_finish(state) +/* CLEANUP_STATE *state; +/* +/* void cleanup_body_edit_free(state) +/* CLEANUP_STATE *state; +/* DESCRIPTION +/* This module maintains queue file regions with body content. +/* Regions are created on the fly, and can be reused multiple +/* times. This module must not be called until the queue file +/* is complete, and there must be no other read/write access +/* to the queue file between the cleanup_body_edit_start() and +/* cleanup_body_edit_finish() calls. +/* +/* cleanup_body_edit_start() performs initialization and sets +/* the queue file write pointer to the start of the first body +/* region. +/* +/* cleanup_body_edit_write() adds a queue file record to the +/* queue file. When the current body region fills up, some +/* unused region is reused, or a new region is created. +/* +/* cleanup_body_edit_finish() makes some final adjustments +/* after the last body content record is written. +/* +/* cleanup_body_edit_free() frees up memory that was allocated +/* by cleanup_body_edit_start() and cleanup_body_edit_write(). +/* +/* Arguments: +/* .IP state +/* Queue file and message processing state. This state is updated +/* as records are processed and as errors happen. +/* .IP type +/* Record type. +/* .IP buf +/* Record content. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <vstream.h> +#include <vstring.h> + +/* Global library. */ + +#include <rec_type.h> +#include <record.h> + +/* Application-specific. */ + +#include <cleanup.h> + +#define LEN(s) VSTRING_LEN(s) + +static int cleanup_body_edit_ptr_rec_len; + +/* cleanup_body_edit_start - rewrite body region pool */ + +int cleanup_body_edit_start(CLEANUP_STATE *state) +{ + const char *myname = "cleanup_body_edit_start"; + CLEANUP_REGION *curr_rp; + + /* + * Calculate the payload size sans body. + */ + state->cont_length = state->body_offset - state->data_offset; + + /* + * One-time initialization. + */ + if (state->body_regions == 0) { + REC_SPACE_NEED(REC_TYPE_PTR_PAYL_SIZE, cleanup_body_edit_ptr_rec_len); + cleanup_region_init(state); + } + + /* + * Return all body regions to the free pool. + */ + cleanup_region_return(state, state->body_regions); + + /* + * Select the first region. XXX This will usually be the original body + * segment, but we must not count on that. Region assignments may change + * when header editing also uses queue file regions. XXX We don't really + * know if the first region will be large enough to hold the first body + * text record, but this problem is so rare that we will not complicate + * the code for it. If the first region is too small then we will simply + * waste it. + */ + curr_rp = state->curr_body_region = state->body_regions = + cleanup_region_open(state, cleanup_body_edit_ptr_rec_len); + + /* + * Link the first body region to the last message header. + */ + if (vstream_fseek(state->dst, state->append_hdr_pt_offset, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + return (-1); + } + state->append_hdr_pt_target = curr_rp->start; + rec_fprintf(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, + (long) state->append_hdr_pt_target); + + /* + * Move the file write pointer to the start of the current region. + */ + if (vstream_ftell(state->dst) != curr_rp->start + && vstream_fseek(state->dst, curr_rp->start, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + return (-1); + } + return (0); +} + +/* cleanup_body_edit_write - add record to body region pool */ + +int cleanup_body_edit_write(CLEANUP_STATE *state, int rec_type, + VSTRING *buf) +{ + const char *myname = "cleanup_body_edit_write"; + CLEANUP_REGION *curr_rp = state->curr_body_region; + CLEANUP_REGION *next_rp; + off_t space_used; + ssize_t space_needed; + ssize_t rec_len; + + if (msg_verbose) + msg_info("%s: where %ld, buflen %ld region start %ld len %ld", + myname, (long) curr_rp->write_offs, (long) LEN(buf), + (long) curr_rp->start, (long) curr_rp->len); + + /* + * Switch to the next body region if we filled up the current one (we + * always append to an open-ended region). Besides space to write the + * specified record, we need to leave space for a final pointer record + * that will link this body region to the next region or to the content + * terminator record. + */ + if (curr_rp->len > 0) { + space_used = curr_rp->write_offs - curr_rp->start; + REC_SPACE_NEED(LEN(buf), rec_len); + space_needed = rec_len + cleanup_body_edit_ptr_rec_len; + if (space_needed > curr_rp->len - space_used) { + + /* + * Update the payload size. Connect the filled up body region to + * its successor. + */ + state->cont_length += space_used; + next_rp = cleanup_region_open(state, space_needed); + if (msg_verbose) + msg_info("%s: link %ld -> %ld", myname, + (long) curr_rp->write_offs, (long) next_rp->start); + rec_fprintf(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, + (long) next_rp->start); + curr_rp->write_offs = vstream_ftell(state->dst); + cleanup_region_close(state, curr_rp); + curr_rp->next = next_rp; + + /* + * Select the new body region. + */ + state->curr_body_region = curr_rp = next_rp; + if (vstream_fseek(state->dst, curr_rp->start, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + return (-1); + } + } + } + + /* + * Finally, output the queue file record. + */ + CLEANUP_OUT_BUF(state, rec_type, buf); + curr_rp->write_offs = vstream_ftell(state->dst); + + return (0); +} + +/* cleanup_body_edit_finish - wrap up body region pool */ + +int cleanup_body_edit_finish(CLEANUP_STATE *state) +{ + CLEANUP_REGION *curr_rp = state->curr_body_region; + + /* + * Update the payload size. + */ + state->cont_length += curr_rp->write_offs - curr_rp->start; + + /* + * Link the last body region to the content terminator record. + */ + rec_fprintf(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, + (long) state->xtra_offset); + curr_rp->write_offs = vstream_ftell(state->dst); + cleanup_region_close(state, curr_rp); + + return (CLEANUP_OUT_OK(state) ? 0 : -1); +} diff --git a/src/cleanup/cleanup_bounce.c b/src/cleanup/cleanup_bounce.c new file mode 100644 index 0000000..361875e --- /dev/null +++ b/src/cleanup/cleanup_bounce.c @@ -0,0 +1,257 @@ +/*++ +/* NAME +/* cleanup_bounce 3 +/* SUMMARY +/* bounce all recipients +/* SYNOPSIS +/* #include "cleanup.h" +/* +/* void cleanup_bounce(state) +/* CLEANUP_STATE *state; +/* DESCRIPTION +/* cleanup_bounce() updates the bounce log on request by client +/* programs that cannot handle such problems themselves. +/* +/* Upon successful completion, all error flags are reset, +/* and the message is scheduled for deletion. +/* Otherwise, the CLEANUP_STAT_WRITE error flag is raised. +/* +/* Arguments: +/* .IP state +/* Queue file and message processing state. This state is +/* updated as records are processed and as errors happen. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> + +/* Utility library. */ + +#include <msg.h> +#include <stringops.h> +#include <stdlib.h> + +/* Global library. */ + +#include <cleanup_user.h> +#include <mail_params.h> +#include <mail_proto.h> +#include <bounce.h> +#include <dsn_util.h> +#include <record.h> +#include <rec_type.h> +#include <dsn_mask.h> +#include <mail_queue.h> +#include <rec_attr_map.h> + +/* Application-specific. */ + +#include "cleanup.h" + +#define STR(x) vstring_str(x) + +/* cleanup_bounce_append - update bounce logfile */ + +static void cleanup_bounce_append(CLEANUP_STATE *state, RECIPIENT *rcpt, + DSN *dsn) +{ + MSG_STATS stats; + + /* + * Don't log a spurious warning (for example, when soft_bounce is turned + * on). bounce_append() already logs a record when the logfile can't be + * updated. Set the write error flag, so that a maildrop queue file won't + * be destroyed. + */ + if (bounce_append(BOUNCE_FLAG_CLEAN, state->queue_id, + CLEANUP_MSG_STATS(&stats, state), + rcpt, "none", dsn) != 0) { + state->errs |= CLEANUP_STAT_WRITE; + } +} + +/* cleanup_bounce - bounce all recipients */ + +int cleanup_bounce(CLEANUP_STATE *state) +{ + const char *myname = "cleanup_bounce"; + VSTRING *buf = vstring_alloc(100); + const CLEANUP_STAT_DETAIL *detail; + DSN_SPLIT dp; + const char *dsn_status; + const char *dsn_text; + char *rcpt = 0; + RECIPIENT recipient; + DSN dsn; + char *attr_name; + char *attr_value; + char *dsn_orcpt = 0; + int dsn_notify = 0; + char *orig_rcpt = 0; + char *start; + int rec_type; + int junk; + long curr_offset; + const char *encoding; + const char *dsn_envid; + int dsn_ret; + int bounce_err; + + /* + * Parse the failure reason if one was given, otherwise use a generic + * mapping from cleanup-internal error code to (DSN + text). + */ + if (state->reason) { + dsn_split(&dp, "5.0.0", state->reason); + dsn_status = DSN_STATUS(dp.dsn); + dsn_text = dp.text; + } else { + detail = cleanup_stat_detail(state->errs); + dsn_status = detail->dsn; + dsn_text = detail->text; + } + + /* + * Create a bounce logfile with one entry for each final recipient. + * Degrade gracefully in case of no recipients or no queue file. + * + * Victor Duchovni observes that the number of recipients in the queue file + * can potentially be very large due to virtual alias expansion. This can + * expand the recipient count by virtual_alias_expansion_limit (default: + * 1000) times. + * + * After a queue file write error, purge any unwritten data (so that + * vstream_fseek() won't fail while trying to flush it) and reset the + * stream error flags to avoid false alarms. + */ + if (vstream_ferror(state->dst) || vstream_fflush(state->dst)) { + (void) vstream_fpurge(state->dst, VSTREAM_PURGE_BOTH); + vstream_clearerr(state->dst); + } + if (vstream_fseek(state->dst, 0L, SEEK_SET) < 0) + msg_fatal("%s: seek %s: %m", myname, cleanup_path); + + while ((state->errs & CLEANUP_STAT_WRITE) == 0) { + if ((curr_offset = vstream_ftell(state->dst)) < 0) + msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path); + if ((rec_type = rec_get(state->dst, buf, 0)) <= 0 + || rec_type == REC_TYPE_END) + break; + start = STR(buf); + if (rec_type == REC_TYPE_ATTR) { + if (split_nameval(STR(buf), &attr_name, &attr_value) != 0 + || *attr_value == 0) + continue; + /* Map attribute names to pseudo record type. */ + if ((junk = rec_attr_map(attr_name)) != 0) { + start = attr_value; + rec_type = junk; + } + } + switch (rec_type) { + case REC_TYPE_DSN_ORCPT: /* RCPT TO ORCPT parameter */ + if (dsn_orcpt != 0) /* can't happen */ + myfree(dsn_orcpt); + dsn_orcpt = mystrdup(start); + break; + case REC_TYPE_DSN_NOTIFY: /* RCPT TO NOTIFY parameter */ + if (alldig(start) && (junk = atoi(start)) > 0 + && DSN_NOTIFY_OK(junk)) + dsn_notify = junk; + else + dsn_notify = 0; + break; + case REC_TYPE_ORCP: /* unmodified RCPT TO address */ + if (orig_rcpt != 0) /* can't happen */ + myfree(orig_rcpt); + orig_rcpt = mystrdup(start); + break; + case REC_TYPE_RCPT: /* rewritten RCPT TO address */ + rcpt = start; + RECIPIENT_ASSIGN(&recipient, curr_offset, + dsn_orcpt ? dsn_orcpt : "", dsn_notify, + orig_rcpt ? orig_rcpt : rcpt, rcpt); + (void) DSN_SIMPLE(&dsn, dsn_status, dsn_text); + cleanup_bounce_append(state, &recipient, &dsn); + /* FALLTHROUGH */ + case REC_TYPE_DRCP: /* canceled recipient */ + case REC_TYPE_DONE: /* can't happen */ + if (orig_rcpt != 0) { + myfree(orig_rcpt); + orig_rcpt = 0; + } + if (dsn_orcpt != 0) { + myfree(dsn_orcpt); + dsn_orcpt = 0; + } + dsn_notify = 0; + break; + } + } + if (orig_rcpt != 0) /* can't happen */ + myfree(orig_rcpt); + if (dsn_orcpt != 0) /* can't happen */ + myfree(dsn_orcpt); + + /* + * No recipients. Yes, this can happen. + */ + if ((state->errs & CLEANUP_STAT_WRITE) == 0 && rcpt == 0) { + RECIPIENT_ASSIGN(&recipient, 0, "", 0, "", "unknown"); + (void) DSN_SIMPLE(&dsn, dsn_status, dsn_text); + cleanup_bounce_append(state, &recipient, &dsn); + } + vstring_free(buf); + + /* + * Flush the bounce logfile to the sender. See also qmgr_active.c. + */ + if ((state->errs & CLEANUP_STAT_WRITE) == 0) { + if ((encoding = nvtable_find(state->attr, MAIL_ATTR_ENCODING)) == 0) + encoding = MAIL_ATTR_ENC_NONE; + dsn_envid = state->dsn_envid ? + state->dsn_envid : ""; + /* Do not send unfiltered (body) content. */ + dsn_ret = (state->errs & (CLEANUP_STAT_CONT | CLEANUP_STAT_SIZE)) ? + DSN_RET_HDRS : state->dsn_ret; + + if (state->verp_delims == 0 || var_verp_bounce_off) { + bounce_err = + bounce_flush(BOUNCE_FLAG_CLEAN, + state->queue_name, state->queue_id, + encoding, state->smtputf8, state->sender, + dsn_envid, dsn_ret); + } else { + bounce_err = + bounce_flush_verp(BOUNCE_FLAG_CLEAN, + state->queue_name, state->queue_id, + encoding, state->smtputf8, state->sender, + dsn_envid, dsn_ret, state->verp_delims); + } + if (bounce_err != 0) { + msg_warn("%s: bounce message failure", state->queue_id); + state->errs |= CLEANUP_STAT_WRITE; + } + } + + /* + * Schedule this message (and trace logfile) for deletion when all is + * well. When all is not well these files would be deleted too, but the + * client would get a different completion status so we have to carefully + * maintain the bits anyway. + */ + if ((state->errs &= CLEANUP_STAT_WRITE) == 0) + state->flags |= CLEANUP_FLAG_DISCARD; + + return (state->errs); +} diff --git a/src/cleanup/cleanup_envelope.c b/src/cleanup/cleanup_envelope.c new file mode 100644 index 0000000..a4b991d --- /dev/null +++ b/src/cleanup/cleanup_envelope.c @@ -0,0 +1,502 @@ +/*++ +/* NAME +/* cleanup_envelope 3 +/* SUMMARY +/* process envelope segment +/* SYNOPSIS +/* #include <cleanup.h> +/* +/* void cleanup_envelope(state, type, buf, len) +/* CLEANUP_STATE *state; +/* int type; +/* const char *buf; +/* ssize_t len; +/* DESCRIPTION +/* This module processes envelope records and writes the result +/* to the queue file. It validates the message structure, rewrites +/* sender/recipient addresses to canonical form, and expands recipients +/* according to entries in the virtual table. This routine absorbs but +/* does not emit the envelope to content boundary record. +/* +/* Arguments: +/* .IP state +/* Queue file and message processing state. This state is updated +/* as records are processed and as errors happen. +/* .IP type +/* Record type. +/* .IP buf +/* Record content. +/* .IP len +/* Record content length. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> /* ssscanf() */ +#include <ctype.h> + +/* Utility library. */ + +#include <msg.h> +#include <vstring.h> +#include <vstream.h> +#include <mymalloc.h> +#include <stringops.h> +#include <nvtable.h> + +/* Global library. */ + +#include <record.h> +#include <rec_type.h> +#include <cleanup_user.h> +#include <qmgr_user.h> +#include <mail_params.h> +#include <verp_sender.h> +#include <mail_proto.h> +#include <dsn_mask.h> +#include <rec_attr_map.h> +#include <smtputf8.h> +#include <deliver_request.h> + +/* Application-specific. */ + +#include "cleanup.h" + +#define STR vstring_str +#define STREQ(x,y) (strcmp((x), (y)) == 0) + +static void cleanup_envelope_process(CLEANUP_STATE *, int, const char *, ssize_t); + +/* cleanup_envelope - initialize message envelope */ + +void cleanup_envelope(CLEANUP_STATE *state, int type, + const char *str, ssize_t len) +{ + + /* + * The message size and count record goes first, so it can easily be + * updated in place. This information takes precedence over any size + * estimate provided by the client. It's all in one record, data size + * first, for backwards compatibility reasons. + */ + cleanup_out_format(state, REC_TYPE_SIZE, REC_TYPE_SIZE_FORMAT, + (REC_TYPE_SIZE_CAST1) 0, /* extra offs - content offs */ + (REC_TYPE_SIZE_CAST2) 0, /* content offset */ + (REC_TYPE_SIZE_CAST3) 0, /* recipient count */ + (REC_TYPE_SIZE_CAST4) 0, /* qmgr options */ + (REC_TYPE_SIZE_CAST5) 0, /* content length */ + (REC_TYPE_SIZE_CAST6) 0); /* smtputf8 */ + + /* + * Pass control to the actual envelope processing routine. + */ + state->action = cleanup_envelope_process; + cleanup_envelope_process(state, type, str, len); +} + +/* cleanup_envelope_process - process one envelope record */ + +static void cleanup_envelope_process(CLEANUP_STATE *state, int type, + const char *buf, ssize_t len) +{ + const char *myname = "cleanup_envelope_process"; + char *attr_name; + char *attr_value; + const char *error_text; + int extra_opts; + int junk; + int mapped_type = type; + const char *mapped_buf = buf; + int milter_count; + +#ifdef DELAY_ACTION + int defer_delay; + +#endif + + if (msg_verbose) + msg_info("initial envelope %c %.*s", type, (int) len, buf); + + if (type == REC_TYPE_FLGS) { + /* Not part of queue file format. */ + extra_opts = atoi(buf); + if (extra_opts & ~CLEANUP_FLAG_MASK_EXTRA) + msg_warn("%s: ignoring bad extra flags: 0x%x", + state->queue_id, extra_opts); + else + state->flags |= extra_opts; + return; + } +#ifdef DELAY_ACTION + if (type == REC_TYPE_DELAY) { + /* Not part of queue file format. */ + defer_delay = atoi(buf); + if (defer_delay <= 0) + msg_warn("%s: ignoring bad delay time: %s", state->queue_id, buf); + else + state->defer_delay = defer_delay; + return; + } +#endif + + /* + * XXX We instantiate a MILTERS structure even when the filter count is + * zero (for example, all filters are in ACCEPT state, or the SMTP server + * sends a dummy MILTERS structure without any filters), otherwise the + * cleanup server would apply the non_smtpd_milters setting + * inappropriately. + */ + if (type == REC_TYPE_MILT_COUNT) { + /* Not part of queue file format. */ + if ((milter_count = atoi(buf)) >= 0) + cleanup_milter_receive(state, milter_count); + return; + } + + /* + * Map DSN attribute name to pseudo record type so that we don't have to + * pollute the queue file with records that are incompatible with past + * Postfix versions. Preferably, people should be able to back out from + * an upgrade without losing mail. + */ + if (type == REC_TYPE_ATTR) { + vstring_strcpy(state->attr_buf, buf); + error_text = split_nameval(STR(state->attr_buf), &attr_name, &attr_value); + if (error_text != 0) { + msg_warn("%s: message rejected: malformed attribute: %s: %.100s", + state->queue_id, error_text, buf); + state->errs |= CLEANUP_STAT_BAD; + return; + } + /* Zero-length values are place holders for unavailable values. */ + if (*attr_value == 0) { + msg_warn("%s: spurious null attribute value for \"%s\" -- ignored", + state->queue_id, attr_name); + return; + } + if ((junk = rec_attr_map(attr_name)) != 0) { + mapped_buf = attr_value; + mapped_type = junk; + } + } + + /* + * Sanity check. + */ + if (strchr(REC_TYPE_ENVELOPE, type) == 0) { + msg_warn("%s: message rejected: unexpected record type %d in envelope", + state->queue_id, type); + state->errs |= CLEANUP_STAT_BAD; + return; + } + + /* + * Although recipient records appear at the end of the initial or + * extracted envelope, the code for processing recipient records is first + * because there can be lots of them. + * + * Recipient records may be mixed with other information (such as FILTER or + * REDIRECT actions from SMTPD). In that case the queue manager needs to + * examine all queue file records before it can start delivery. This is + * not a problem when SMTPD recipient lists are small. + * + * However, if recipient records are not mixed with other records + * (typically, mailing list mail) then we can make an optimization: the + * queue manager does not need to examine every envelope record before it + * can start deliveries. This can help with very large mailing lists. + */ + + /* + * On the transition from non-recipient records to recipient records, + * emit some records and do some sanity checks. + * + * XXX Moving the envelope sender (and the test for its presence) to the + * extracted segment can reduce qmqpd memory requirements because it no + * longer needs to read the entire message into main memory. + */ + if ((state->flags & CLEANUP_FLAG_INRCPT) == 0 + && strchr(REC_TYPE_ENV_RECIPIENT, type) != 0) { + if (state->sender == 0) { + msg_warn("%s: message rejected: missing sender envelope record", + state->queue_id); + state->errs |= CLEANUP_STAT_BAD; + return; + } + if (state->arrival_time.tv_sec == 0) { + msg_warn("%s: message rejected: missing time envelope record", + state->queue_id); + state->errs |= CLEANUP_STAT_BAD; + return; + } + + /* + * XXX This works by accident, because the sender is recorded at the + * beginning of the envelope segment. + */ + if ((state->flags & CLEANUP_FLAG_WARN_SEEN) == 0 + && state->sender && *state->sender + && var_delay_warn_time > 0) { + cleanup_out_format(state, REC_TYPE_WARN, REC_TYPE_WARN_FORMAT, + REC_TYPE_WARN_ARG(state->arrival_time.tv_sec + + var_delay_warn_time)); + } + state->flags |= CLEANUP_FLAG_INRCPT; + } + + /* + * Initial envelope recipient record processing. + */ + if (type == REC_TYPE_RCPT) { + if (state->sender == 0) { /* protect showq */ + msg_warn("%s: message rejected: envelope recipient precedes sender", + state->queue_id); + state->errs |= CLEANUP_STAT_BAD; + return; + } + if (state->orig_rcpt == 0) + state->orig_rcpt = mystrdup(buf); + cleanup_addr_recipient(state, buf); + if (cleanup_milters != 0 + && state->milters == 0 + && CLEANUP_MILTER_OK(state)) + cleanup_milter_emul_rcpt(state, cleanup_milters, state->recip); + myfree(state->orig_rcpt); + state->orig_rcpt = 0; + if (state->dsn_orcpt != 0) { + myfree(state->dsn_orcpt); + state->dsn_orcpt = 0; + } + state->dsn_notify = 0; + return; + } + if (type == REC_TYPE_DONE || type == REC_TYPE_DRCP) { + if (state->orig_rcpt != 0) { + myfree(state->orig_rcpt); + state->orig_rcpt = 0; + } + if (state->dsn_orcpt != 0) { + myfree(state->dsn_orcpt); + state->dsn_orcpt = 0; + } + state->dsn_notify = 0; + return; + } + if (mapped_type == REC_TYPE_DSN_ORCPT) { + if (state->dsn_orcpt) { + msg_warn("%s: ignoring out-of-order DSN original recipient record <%.200s>", + state->queue_id, state->dsn_orcpt); + myfree(state->dsn_orcpt); + } + state->dsn_orcpt = mystrdup(mapped_buf); + return; + } + if (mapped_type == REC_TYPE_DSN_NOTIFY) { + if (state->dsn_notify) { + msg_warn("%s: ignoring out-of-order DSN notify record <%d>", + state->queue_id, state->dsn_notify); + state->dsn_notify = 0; + } + if (!alldig(mapped_buf) || (junk = atoi(mapped_buf)) == 0 + || DSN_NOTIFY_OK(junk) == 0) + msg_warn("%s: ignoring malformed DSN notify record <%.200s>", + state->queue_id, buf); + else + state->qmgr_opts |= + QMGR_READ_FLAG_FROM_DSN(state->dsn_notify = junk); + return; + } + if (type == REC_TYPE_ORCP) { + if (state->orig_rcpt != 0) { + msg_warn("%s: ignoring out-of-order original recipient record <%.200s>", + state->queue_id, state->orig_rcpt); + myfree(state->orig_rcpt); + } + state->orig_rcpt = mystrdup(buf); + return; + } + if (type == REC_TYPE_MESG) { + state->action = cleanup_message; + if (state->flags & CLEANUP_FLAG_INRCPT) { + if (state->milters || cleanup_milters) { + /* Make room to append recipient. */ + if ((state->append_rcpt_pt_offset = vstream_ftell(state->dst)) < 0) + msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); + cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L); + if ((state->append_rcpt_pt_target = vstream_ftell(state->dst)) < 0) + msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); + } + state->flags &= ~CLEANUP_FLAG_INRCPT; + } + return; + } + + /* + * Initial envelope non-recipient record processing. + * + * If the message was requeued with "postsuper -r" use their + * SMTPUTF8_REQUESTED flag. + */ + if (state->flags & CLEANUP_FLAG_INRCPT) + /* Tell qmgr that recipient records are mixed with other information. */ + state->qmgr_opts |= QMGR_READ_FLAG_MIXED_RCPT_OTHER; + if (type == REC_TYPE_SIZE) { + /* Use our own SIZE record, except for the SMTPUTF8_REQUESTED flag. */ + (void) sscanf(buf, "%*s $*s %*s %*s %*s %d", &state->smtputf8); + state->smtputf8 &= SMTPUTF8_FLAG_REQUESTED; + return; + } + if (mapped_type == REC_TYPE_CTIME) + /* Use our own expiration time base record instead. */ + return; + if (type == REC_TYPE_TIME) { + /* First instance wins. */ + if (state->arrival_time.tv_sec == 0) { + REC_TYPE_TIME_SCAN(buf, state->arrival_time); + cleanup_out(state, type, buf, len); + } + /* Generate our own expiration time base record. */ + cleanup_out_format(state, REC_TYPE_ATTR, "%s=%ld", + MAIL_ATTR_CREATE_TIME, (long) time((time_t *) 0)); + return; + } + if (type == REC_TYPE_FULL) { + /* First instance wins. */ + if (state->fullname == 0) { + state->fullname = mystrdup(buf); + cleanup_out(state, type, buf, len); + } + return; + } + if (type == REC_TYPE_FROM) { + off_t after_sender_offs; + + /* Allow only one instance. */ + if (state->sender != 0) { + msg_warn("%s: message rejected: multiple envelope sender records", + state->queue_id); + state->errs |= CLEANUP_STAT_BAD; + return; + } + if (state->milters || cleanup_milters) { + /* Remember the sender record offset. */ + if ((state->sender_pt_offset = vstream_ftell(state->dst)) < 0) + msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); + } + after_sender_offs = cleanup_addr_sender(state, buf); + if (state->milters || cleanup_milters) { + /* Remember the after-sender record offset. */ + state->sender_pt_target = after_sender_offs; + } + if (cleanup_milters != 0 + && state->milters == 0 + && CLEANUP_MILTER_OK(state)) + cleanup_milter_emul_mail(state, cleanup_milters, state->sender); + return; + } + if (mapped_type == REC_TYPE_DSN_ENVID) { + /* Don't break "postsuper -r" after Milter overrides ENVID. */ + if (!allprint(mapped_buf)) { + msg_warn("%s: message rejected: bad DSN envelope ID record", + state->queue_id); + state->errs |= CLEANUP_STAT_BAD; + return; + } + if (state->dsn_envid != 0) + myfree(state->dsn_envid); + state->dsn_envid = mystrdup(mapped_buf); + cleanup_out(state, type, buf, len); + return; + } + if (mapped_type == REC_TYPE_DSN_RET) { + /* Don't break "postsuper -r" after Milter overrides RET. */ + if (!alldig(mapped_buf) || (junk = atoi(mapped_buf)) == 0 + || DSN_RET_OK(junk) == 0) { + msg_warn("%s: message rejected: bad DSN RET record <%.200s>", + state->queue_id, buf); + state->errs |= CLEANUP_STAT_BAD; + return; + } + state->dsn_ret = junk; + cleanup_out(state, type, buf, len); + return; + } + if (type == REC_TYPE_WARN) { + /* First instance wins. */ + if ((state->flags & CLEANUP_FLAG_WARN_SEEN) == 0) { + state->flags |= CLEANUP_FLAG_WARN_SEEN; + cleanup_out(state, type, buf, len); + } + return; + } + /* XXX Needed for cleanup_bounce(); sanity check usage. */ + if (type == REC_TYPE_VERP) { + if (state->verp_delims == 0) { + if (state->sender == 0 || state->sender[0] == 0) { + msg_warn("%s: ignoring VERP request for null sender", + state->queue_id); + } else if (verp_delims_verify(buf) != 0) { + msg_warn("%s: ignoring bad VERP request: \"%.100s\"", + state->queue_id, buf); + } else { + state->verp_delims = mystrdup(buf); + cleanup_out(state, type, buf, len); + } + } + return; + } + if (type == REC_TYPE_ATTR) { + if (state->attr->used >= var_qattr_count_limit) { + msg_warn("%s: message rejected: attribute count exceeds limit %d", + state->queue_id, var_qattr_count_limit); + state->errs |= CLEANUP_STAT_BAD; + return; + } + if (strcmp(attr_name, MAIL_ATTR_RWR_CONTEXT) == 0) { + /* Choose header rewriting context. See also cleanup_addr.c. */ + if (STREQ(attr_value, MAIL_ATTR_RWR_LOCAL)) { + state->hdr_rewrite_context = MAIL_ATTR_RWR_LOCAL; + } else if (STREQ(attr_value, MAIL_ATTR_RWR_REMOTE)) { + state->hdr_rewrite_context = + (*var_remote_rwr_domain ? MAIL_ATTR_RWR_REMOTE : 0); + } else { + msg_warn("%s: message rejected: bad rewriting context: %.100s", + state->queue_id, attr_value); + state->errs |= CLEANUP_STAT_BAD; + return; + } + } + if (strcmp(attr_name, MAIL_ATTR_TRACE_FLAGS) == 0) { + if (!alldig(attr_value)) { + msg_warn("%s: message rejected: bad TFLAG record <%.200s>", + state->queue_id, buf); + state->errs |= CLEANUP_STAT_BAD; + return; + } + if (state->tflags == 0) + state->tflags = DEL_REQ_TRACE_FLAGS(atoi(attr_value)); + } + nvtable_update(state->attr, attr_name, attr_value); + cleanup_out(state, type, buf, len); + return; + } else { + cleanup_out(state, type, buf, len); + return; + } +} diff --git a/src/cleanup/cleanup_extracted.c b/src/cleanup/cleanup_extracted.c new file mode 100644 index 0000000..e6c2122 --- /dev/null +++ b/src/cleanup/cleanup_extracted.c @@ -0,0 +1,328 @@ +/*++ +/* NAME +/* cleanup_extracted 3 +/* SUMMARY +/* process extracted segment +/* SYNOPSIS +/* #include "cleanup.h" +/* +/* void cleanup_extracted(state, type, buf, len) +/* CLEANUP_STATE *state; +/* int type; +/* const char *buf; +/* ssize_t len; +/* DESCRIPTION +/* This module processes message records with information extracted +/* from message content, or with recipients that are stored after the +/* message content. It updates recipient records, writes extracted +/* information records to the output, and writes the queue +/* file end marker. The queue file is left in a state that +/* is suitable for Milter inspection, but the size record still +/* contains dummy values. +/* +/* Arguments: +/* .IP state +/* Queue file and message processing state. This state is updated +/* as records are processed and as errors happen. +/* .IP type +/* Record type. +/* .IP buf +/* Record content. +/* .IP len +/* Record content length. +/* 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 <errno.h> +#include <string.h> +#include <stdlib.h> + +/* Utility library. */ + +#include <msg.h> +#include <vstring.h> +#include <vstream.h> +#include <mymalloc.h> +#include <nvtable.h> +#include <stringops.h> + +/* Global library. */ + +#include <cleanup_user.h> +#include <qmgr_user.h> +#include <record.h> +#include <rec_type.h> +#include <mail_params.h> +#include <mail_proto.h> +#include <dsn_mask.h> +#include <rec_attr_map.h> + +/* Application-specific. */ + +#include "cleanup.h" + +#define STR(x) vstring_str(x) + +static void cleanup_extracted_process(CLEANUP_STATE *, int, const char *, ssize_t); +static void cleanup_extracted_finish(CLEANUP_STATE *); + +/* cleanup_extracted - initialize extracted segment */ + +void cleanup_extracted(CLEANUP_STATE *state, int type, + const char *buf, ssize_t len) +{ + + /* + * Start the extracted segment. + */ + cleanup_out_string(state, REC_TYPE_XTRA, ""); + + /* + * Pass control to the actual envelope processing routine. + */ + state->action = cleanup_extracted_process; + cleanup_extracted_process(state, type, buf, len); +} + +/* cleanup_extracted_process - process one extracted envelope record */ + +void cleanup_extracted_process(CLEANUP_STATE *state, int type, + const char *buf, ssize_t len) +{ + const char *myname = "cleanup_extracted_process"; + const char *encoding; + char *attr_name; + char *attr_value; + const char *error_text; + int extra_opts; + int junk; + +#ifdef DELAY_ACTION + int defer_delay; + +#endif + + if (msg_verbose) + msg_info("extracted envelope %c %.*s", type, (int) len, buf); + + if (type == REC_TYPE_FLGS) { + /* Not part of queue file format. */ + extra_opts = atoi(buf); + if (extra_opts & ~CLEANUP_FLAG_MASK_EXTRA) + msg_warn("%s: ignoring bad extra flags: 0x%x", + state->queue_id, extra_opts); + else + state->flags |= extra_opts; + return; + } +#ifdef DELAY_ACTION + if (type == REC_TYPE_DELAY) { + /* Not part of queue file format. */ + defer_delay = atoi(buf); + if (defer_delay <= 0) + msg_warn("%s: ignoring bad delay time: %s", state->queue_id, buf); + else + state->defer_delay = defer_delay; + return; + } +#endif + + if (strchr(REC_TYPE_EXTRACT, type) == 0) { + msg_warn("%s: message rejected: " + "unexpected record type %d in extracted envelope", + state->queue_id, type); + state->errs |= CLEANUP_STAT_BAD; + return; + } + + /* + * Map DSN attribute name to pseudo record type so that we don't have to + * pollute the queue file with records that are incompatible with past + * Postfix versions. Preferably, people should be able to back out from + * an upgrade without losing mail. + */ + if (type == REC_TYPE_ATTR) { + vstring_strcpy(state->attr_buf, buf); + error_text = split_nameval(STR(state->attr_buf), &attr_name, &attr_value); + if (error_text != 0) { + msg_warn("%s: message rejected: malformed attribute: %s: %.100s", + state->queue_id, error_text, buf); + state->errs |= CLEANUP_STAT_BAD; + return; + } + /* Zero-length values are place holders for unavailable values. */ + if (*attr_value == 0) { + msg_warn("%s: spurious null attribute value for \"%s\" -- ignored", + state->queue_id, attr_name); + return; + } + if ((junk = rec_attr_map(attr_name)) != 0) { + buf = attr_value; + type = junk; + } + } + + /* + * On the transition from non-recipient records to recipient records, + * emit optional information from header/body content. + */ + if ((state->flags & CLEANUP_FLAG_INRCPT) == 0 + && strchr(REC_TYPE_EXT_RECIPIENT, type) != 0) { + if (state->filter != 0) + cleanup_out_string(state, REC_TYPE_FILT, state->filter); + if (state->redirect != 0) + cleanup_out_string(state, REC_TYPE_RDR, state->redirect); + if ((encoding = nvtable_find(state->attr, MAIL_ATTR_ENCODING)) != 0) + cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s", + MAIL_ATTR_ENCODING, encoding); + state->flags |= CLEANUP_FLAG_INRCPT; + /* Make room to append more meta records. */ + if (state->milters || cleanup_milters) { + if ((state->append_meta_pt_offset = vstream_ftell(state->dst)) < 0) + msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); + cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L); + if ((state->append_meta_pt_target = vstream_ftell(state->dst)) < 0) + msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); + } + } + + /* + * Extracted envelope recipient record processing. + */ + if (type == REC_TYPE_RCPT) { + if (state->sender == 0) { /* protect showq */ + msg_warn("%s: message rejected: envelope recipient precedes sender", + state->queue_id); + state->errs |= CLEANUP_STAT_BAD; + return; + } + if (state->orig_rcpt == 0) + state->orig_rcpt = mystrdup(buf); + cleanup_addr_recipient(state, buf); + if (cleanup_milters != 0 + && state->milters == 0 + && CLEANUP_MILTER_OK(state)) + cleanup_milter_emul_rcpt(state, cleanup_milters, state->recip); + myfree(state->orig_rcpt); + state->orig_rcpt = 0; + if (state->dsn_orcpt != 0) { + myfree(state->dsn_orcpt); + state->dsn_orcpt = 0; + } + state->dsn_notify = 0; + return; + } + if (type == REC_TYPE_DONE || type == REC_TYPE_DRCP) { + if (state->orig_rcpt != 0) { + myfree(state->orig_rcpt); + state->orig_rcpt = 0; + } + if (state->dsn_orcpt != 0) { + myfree(state->dsn_orcpt); + state->dsn_orcpt = 0; + } + state->dsn_notify = 0; + return; + } + if (type == REC_TYPE_DSN_ORCPT) { + if (state->dsn_orcpt) { + msg_warn("%s: ignoring out-of-order DSN original recipient record <%.200s>", + state->queue_id, state->dsn_orcpt); + myfree(state->dsn_orcpt); + } + state->dsn_orcpt = mystrdup(buf); + return; + } + if (type == REC_TYPE_DSN_NOTIFY) { + if (state->dsn_notify) { + msg_warn("%s: ignoring out-of-order DSN notify record <%d>", + state->queue_id, state->dsn_notify); + state->dsn_notify = 0; + } + if (!alldig(buf) || (junk = atoi(buf)) == 0 || DSN_NOTIFY_OK(junk) == 0) + msg_warn("%s: ignoring malformed dsn notify record <%.200s>", + state->queue_id, buf); + else + state->qmgr_opts |= + QMGR_READ_FLAG_FROM_DSN(state->dsn_notify = junk); + return; + } + if (type == REC_TYPE_ORCP) { + if (state->orig_rcpt != 0) { + msg_warn("%s: ignoring out-of-order original recipient record <%.200s>", + state->queue_id, buf); + myfree(state->orig_rcpt); + } + state->orig_rcpt = mystrdup(buf); + return; + } + if (type == REC_TYPE_END) { + /* Make room to append recipient. */ + if ((state->milters || cleanup_milters) + && state->append_rcpt_pt_offset < 0) { + if ((state->append_rcpt_pt_offset = vstream_ftell(state->dst)) < 0) + msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); + cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L); + if ((state->append_rcpt_pt_target = vstream_ftell(state->dst)) < 0) + msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); + } + state->flags &= ~CLEANUP_FLAG_INRCPT; + state->flags |= CLEANUP_FLAG_END_SEEN; + cleanup_extracted_finish(state); + return; + } + + /* + * Extracted envelope non-recipient record processing. + */ + if (state->flags & CLEANUP_FLAG_INRCPT) + /* Tell qmgr that recipient records are mixed with other information. */ + state->qmgr_opts |= QMGR_READ_FLAG_MIXED_RCPT_OTHER; + cleanup_out(state, type, buf, len); + return; +} + +/* cleanup_extracted_finish - complete the third message segment */ + +void cleanup_extracted_finish(CLEANUP_STATE *state) +{ + + /* + * On the way out, add the optional automatic BCC recipient. + */ + if ((state->flags & CLEANUP_FLAG_BCC_OK) + && state->recip != 0 && *var_always_bcc) + cleanup_addr_bcc(state, var_always_bcc); + + /* + * Flush non-Milter header/body_checks BCC recipients. Clear hbc_rcpt + * so that it can be used for other purposes. + */ + if (state->hbc_rcpt) { + if (CLEANUP_OUT_OK(state) && state->recip != 0) { + char **cpp; + + for (cpp = state->hbc_rcpt->argv; *cpp; cpp++) + cleanup_addr_bcc(state, *cpp); + } + argv_free(state->hbc_rcpt); + state->hbc_rcpt = 0; + } + + /* + * Terminate the extracted segment. + */ + cleanup_out_string(state, REC_TYPE_END, ""); +} diff --git a/src/cleanup/cleanup_final.c b/src/cleanup/cleanup_final.c new file mode 100644 index 0000000..db77fc7 --- /dev/null +++ b/src/cleanup/cleanup_final.c @@ -0,0 +1,78 @@ +/*++ +/* NAME +/* cleanup_final 3 +/* SUMMARY +/* finalize queue file +/* SYNOPSIS +/* #include "cleanup.h" +/* +/* void cleanup_final(state) +/* CLEANUP_STATE *state; +/* DESCRIPTION +/* cleanup_final() performs final queue file content (not +/* attribute) updates so that the file is ready to be closed. +/* 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 <errno.h> + +/* Utility library. */ + +#include <msg.h> + +/* Global library. */ + +#include <cleanup_user.h> +#include <rec_type.h> + +/* Application-specific. */ + +#include "cleanup.h" + +/* cleanup_final - final queue file content updates */ + +void cleanup_final(CLEANUP_STATE *state) +{ + const char *myname = "cleanup_final"; + + /* + * vstream_fseek() would flush the buffer anyway, but the code just reads + * better if we flush first, because it makes seek error handling more + * straightforward. + */ + if (vstream_fflush(state->dst)) { + if (errno == EFBIG) { + msg_warn("%s: queue file size limit exceeded", state->queue_id); + state->errs |= CLEANUP_STAT_SIZE; + } else { + msg_warn("%s: write queue file: %m", state->queue_id); + state->errs |= CLEANUP_STAT_WRITE; + } + return; + } + + /* + * Update the preliminary message size and count fields with the actual + * values. + */ + if (vstream_fseek(state->dst, 0L, SEEK_SET) < 0) + msg_fatal("%s: vstream_fseek %s: %m", myname, cleanup_path); + cleanup_out_format(state, REC_TYPE_SIZE, REC_TYPE_SIZE_FORMAT, + (REC_TYPE_SIZE_CAST1) (state->xtra_offset - state->data_offset), + (REC_TYPE_SIZE_CAST2) state->data_offset, + (REC_TYPE_SIZE_CAST3) state->rcpt_count, + (REC_TYPE_SIZE_CAST4) state->qmgr_opts, + (REC_TYPE_SIZE_CAST5) state->cont_length, + (REC_TYPE_SIZE_CAST6) state->smtputf8); +} diff --git a/src/cleanup/cleanup_init.c b/src/cleanup/cleanup_init.c new file mode 100644 index 0000000..5719f0a --- /dev/null +++ b/src/cleanup/cleanup_init.c @@ -0,0 +1,483 @@ +/*++ +/* NAME +/* cleanup_init 3 +/* SUMMARY +/* cleanup callable interface, initializations +/* SYNOPSIS +/* #include "cleanup.h" +/* +/* CONFIG_INT_TABLE cleanup_int_table[]; +/* +/* CONFIG_BOOL_TABLE cleanup_bool_table[]; +/* +/* CONFIG_STR_TABLE cleanup_str_table[]; +/* +/* CONFIG_TIME_TABLE cleanup_time_table[]; +/* +/* void cleanup_pre_jail(service_name, argv) +/* char *service_name; +/* char **argv; +/* +/* void cleanup_post_jail(service_name, argv) +/* char *service_name; +/* char **argv; +/* +/* char *cleanup_path; +/* VSTRING *cleanup_trace_path; +/* +/* void cleanup_all() +/* +/* void cleanup_sig(sigval) +/* int sigval; +/* DESCRIPTION +/* This module implements a callable interface to the cleanup service +/* for one-time initializations that must be done before any message +/* processing can take place. +/* +/* cleanup_{int,str,time}_table[] specify configuration +/* parameters that must be initialized before calling any functions +/* in this module. These tables satisfy the interface as specified in +/* single_service(3). +/* +/* cleanup_pre_jail() and cleanup_post_jail() perform mandatory +/* initializations before and after the process enters the optional +/* chroot jail. These functions satisfy the interface as specified +/* in single_service(3). +/* +/* cleanup_path is either a null pointer or it is the name of a queue +/* file that currently is being written. This information is used +/* by cleanup_all() to remove incomplete files after a fatal error, +/* or by cleanup_sig() after arrival of a SIGTERM signal. +/* +/* cleanup_trace_path is either a null pointer or the pathname of a +/* trace logfile with DSN SUCCESS notifications. This information is +/* used to remove a trace file when the mail transaction is canceled. +/* +/* cleanup_all() must be called in case of fatal error, in order +/* to remove an incomplete queue file. +/* +/* cleanup_sig() must be called in case of SIGTERM, in order +/* to remove an incomplete queue file. +/* DIAGNOSTICS +/* Problems and transactions are logged to \fBsyslogd\fR(8) +/* or \fBpostlogd\fR(8). +/* SEE ALSO +/* cleanup_api(3) cleanup callable interface, message processing +/* 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 <signal.h> +#include <string.h> + +/* Utility library. */ + +#include <msg.h> +#include <iostuff.h> +#include <name_code.h> +#include <name_mask.h> +#include <stringops.h> + +/* Global library. */ + +#include <mail_addr.h> +#include <mail_params.h> +#include <mail_version.h> /* milter_macro_v */ +#include <ext_prop.h> +#include <flush_clnt.h> + +/* Application-specific. */ + +#include "cleanup.h" + + /* + * Global state: any queue files that we have open, so that the error + * handler can clean up in case of trouble. + */ +char *cleanup_path; /* queue file name */ + + /* + * Another piece of global state: pathnames of partial bounce or trace + * logfiles that need to be cleaned up when the cleanup request is aborted. + */ +VSTRING *cleanup_trace_path; + + /* + * Tunable parameters. + */ +int var_hopcount_limit; /* max mailer hop count */ +char *var_canonical_maps; /* common canonical maps */ +char *var_send_canon_maps; /* sender canonical maps */ +char *var_rcpt_canon_maps; /* recipient canonical maps */ +char *var_canon_classes; /* what to canonicalize */ +char *var_send_canon_classes; /* what sender to canonicalize */ +char *var_rcpt_canon_classes; /* what recipient to canonicalize */ +char *var_virt_alias_maps; /* virtual alias maps */ +char *var_masq_domains; /* masquerade domains */ +char *var_masq_exceptions; /* users not masqueraded */ +char *var_header_checks; /* primary header checks */ +char *var_mimehdr_checks; /* mime header checks */ +char *var_nesthdr_checks; /* nested header checks */ +char *var_body_checks; /* any body checks */ +int var_dup_filter_limit; /* recipient dup filter */ +char *var_empty_addr; /* destination of bounced bounces */ +int var_delay_warn_time; /* delay that triggers warning */ +char *var_prop_extension; /* propagate unmatched extension */ +char *var_always_bcc; /* big brother */ +char *var_rcpt_witheld; /* recipients not disclosed */ +char *var_masq_classes; /* what to masquerade */ +int var_qattr_count_limit; /* named attribute limit */ +int var_virt_recur_limit; /* maximum virtual alias recursion */ +int var_virt_expan_limit; /* maximum virtual alias expansion */ +int var_body_check_len; /* when to stop body scan */ +char *var_send_bcc_maps; /* sender auto-bcc maps */ +char *var_rcpt_bcc_maps; /* recipient auto-bcc maps */ +char *var_remote_rwr_domain; /* header-only surrogate */ +char *var_msg_reject_chars; /* reject these characters */ +char *var_msg_strip_chars; /* strip these characters */ +int var_verp_bounce_off; /* don't verp the bounces */ +int var_milt_conn_time; /* milter connect/handshake timeout */ +int var_milt_cmd_time; /* milter command timeout */ +int var_milt_msg_time; /* milter content timeout */ +char *var_milt_protocol; /* Sendmail 8 milter protocol */ +char *var_milt_def_action; /* default milter action */ +char *var_milt_daemon_name; /* {daemon_name} macro value */ +char *var_milt_v; /* {v} macro value */ +char *var_milt_conn_macros; /* connect macros */ +char *var_milt_helo_macros; /* HELO macros */ +char *var_milt_mail_macros; /* MAIL FROM macros */ +char *var_milt_rcpt_macros; /* RCPT TO macros */ +char *var_milt_data_macros; /* DATA macros */ +char *var_milt_eoh_macros; /* end-of-header macros */ +char *var_milt_eod_macros; /* end-of-data macros */ +char *var_milt_unk_macros; /* unknown command macros */ +char *var_cleanup_milters; /* non-SMTP mail */ +char *var_milt_head_checks; /* post-Milter header checks */ +char *var_milt_macro_deflts; /* default macro settings */ +int var_auto_8bit_enc_hdr; /* auto-detect 8bit encoding header */ +int var_always_add_hdrs; /* always add missing headers */ +int var_virt_addrlen_limit; /* stop exponential growth */ +char *var_hfrom_format; /* header_from_format */ +int var_cleanup_mask_stray_cr_lf; /* replace stray CR or LF with space */ + +const CONFIG_INT_TABLE cleanup_int_table[] = { + VAR_HOPCOUNT_LIMIT, DEF_HOPCOUNT_LIMIT, &var_hopcount_limit, 1, 0, + VAR_DUP_FILTER_LIMIT, DEF_DUP_FILTER_LIMIT, &var_dup_filter_limit, 0, 0, + VAR_QATTR_COUNT_LIMIT, DEF_QATTR_COUNT_LIMIT, &var_qattr_count_limit, 1, 0, + VAR_VIRT_RECUR_LIMIT, DEF_VIRT_RECUR_LIMIT, &var_virt_recur_limit, 1, 0, + VAR_VIRT_EXPAN_LIMIT, DEF_VIRT_EXPAN_LIMIT, &var_virt_expan_limit, 1, 0, + VAR_VIRT_ADDRLEN_LIMIT, DEF_VIRT_ADDRLEN_LIMIT, &var_virt_addrlen_limit, 1, 0, + VAR_BODY_CHECK_LEN, DEF_BODY_CHECK_LEN, &var_body_check_len, 0, 0, + 0, +}; + +const CONFIG_BOOL_TABLE cleanup_bool_table[] = { + VAR_VERP_BOUNCE_OFF, DEF_VERP_BOUNCE_OFF, &var_verp_bounce_off, + VAR_AUTO_8BIT_ENC_HDR, DEF_AUTO_8BIT_ENC_HDR, &var_auto_8bit_enc_hdr, + VAR_ALWAYS_ADD_HDRS, DEF_ALWAYS_ADD_HDRS, &var_always_add_hdrs, + VAR_CLEANUP_MASK_STRAY_CR_LF, DEF_CLEANUP_MASK_STRAY_CR_LF, &var_cleanup_mask_stray_cr_lf, + 0, +}; + +const CONFIG_TIME_TABLE cleanup_time_table[] = { + VAR_DELAY_WARN_TIME, DEF_DELAY_WARN_TIME, &var_delay_warn_time, 0, 0, + VAR_MILT_CONN_TIME, DEF_MILT_CONN_TIME, &var_milt_conn_time, 1, 0, + VAR_MILT_CMD_TIME, DEF_MILT_CMD_TIME, &var_milt_cmd_time, 1, 0, + VAR_MILT_MSG_TIME, DEF_MILT_MSG_TIME, &var_milt_msg_time, 1, 0, + 0, +}; + +const CONFIG_STR_TABLE cleanup_str_table[] = { + VAR_CANONICAL_MAPS, DEF_CANONICAL_MAPS, &var_canonical_maps, 0, 0, + VAR_SEND_CANON_MAPS, DEF_SEND_CANON_MAPS, &var_send_canon_maps, 0, 0, + VAR_RCPT_CANON_MAPS, DEF_RCPT_CANON_MAPS, &var_rcpt_canon_maps, 0, 0, + VAR_CANON_CLASSES, DEF_CANON_CLASSES, &var_canon_classes, 1, 0, + VAR_SEND_CANON_CLASSES, DEF_SEND_CANON_CLASSES, &var_send_canon_classes, 1, 0, + VAR_RCPT_CANON_CLASSES, DEF_RCPT_CANON_CLASSES, &var_rcpt_canon_classes, 1, 0, + VAR_VIRT_ALIAS_MAPS, DEF_VIRT_ALIAS_MAPS, &var_virt_alias_maps, 0, 0, + VAR_MASQ_DOMAINS, DEF_MASQ_DOMAINS, &var_masq_domains, 0, 0, + VAR_EMPTY_ADDR, DEF_EMPTY_ADDR, &var_empty_addr, 1, 0, + VAR_MASQ_EXCEPTIONS, DEF_MASQ_EXCEPTIONS, &var_masq_exceptions, 0, 0, + VAR_HEADER_CHECKS, DEF_HEADER_CHECKS, &var_header_checks, 0, 0, + VAR_MIMEHDR_CHECKS, DEF_MIMEHDR_CHECKS, &var_mimehdr_checks, 0, 0, + VAR_NESTHDR_CHECKS, DEF_NESTHDR_CHECKS, &var_nesthdr_checks, 0, 0, + VAR_BODY_CHECKS, DEF_BODY_CHECKS, &var_body_checks, 0, 0, + VAR_PROP_EXTENSION, DEF_PROP_EXTENSION, &var_prop_extension, 0, 0, + VAR_ALWAYS_BCC, DEF_ALWAYS_BCC, &var_always_bcc, 0, 0, + VAR_RCPT_WITHELD, DEF_RCPT_WITHELD, &var_rcpt_witheld, 0, 0, + VAR_MASQ_CLASSES, DEF_MASQ_CLASSES, &var_masq_classes, 0, 0, + VAR_SEND_BCC_MAPS, DEF_SEND_BCC_MAPS, &var_send_bcc_maps, 0, 0, + VAR_RCPT_BCC_MAPS, DEF_RCPT_BCC_MAPS, &var_rcpt_bcc_maps, 0, 0, + VAR_REM_RWR_DOMAIN, DEF_REM_RWR_DOMAIN, &var_remote_rwr_domain, 0, 0, + VAR_MSG_REJECT_CHARS, DEF_MSG_REJECT_CHARS, &var_msg_reject_chars, 0, 0, + VAR_MSG_STRIP_CHARS, DEF_MSG_STRIP_CHARS, &var_msg_strip_chars, 0, 0, + VAR_MILT_PROTOCOL, DEF_MILT_PROTOCOL, &var_milt_protocol, 1, 0, + VAR_MILT_DEF_ACTION, DEF_MILT_DEF_ACTION, &var_milt_def_action, 1, 0, + VAR_MILT_DAEMON_NAME, DEF_MILT_DAEMON_NAME, &var_milt_daemon_name, 1, 0, + VAR_MILT_V, DEF_MILT_V, &var_milt_v, 1, 0, + VAR_MILT_CONN_MACROS, DEF_MILT_CONN_MACROS, &var_milt_conn_macros, 0, 0, + VAR_MILT_HELO_MACROS, DEF_MILT_HELO_MACROS, &var_milt_helo_macros, 0, 0, + VAR_MILT_MAIL_MACROS, DEF_MILT_MAIL_MACROS, &var_milt_mail_macros, 0, 0, + VAR_MILT_RCPT_MACROS, DEF_MILT_RCPT_MACROS, &var_milt_rcpt_macros, 0, 0, + VAR_MILT_DATA_MACROS, DEF_MILT_DATA_MACROS, &var_milt_data_macros, 0, 0, + VAR_MILT_EOH_MACROS, DEF_MILT_EOH_MACROS, &var_milt_eoh_macros, 0, 0, + VAR_MILT_EOD_MACROS, DEF_MILT_EOD_MACROS, &var_milt_eod_macros, 0, 0, + VAR_MILT_UNK_MACROS, DEF_MILT_UNK_MACROS, &var_milt_unk_macros, 0, 0, + VAR_CLEANUP_MILTERS, DEF_CLEANUP_MILTERS, &var_cleanup_milters, 0, 0, + VAR_MILT_HEAD_CHECKS, DEF_MILT_HEAD_CHECKS, &var_milt_head_checks, 0, 0, + VAR_MILT_MACRO_DEFLTS, DEF_MILT_MACRO_DEFLTS, &var_milt_macro_deflts, 0, 0, + VAR_HFROM_FORMAT, DEF_HFROM_FORMAT, &var_hfrom_format, 1, 0, + 0, +}; + + /* + * Mappings. + */ +MAPS *cleanup_comm_canon_maps; +MAPS *cleanup_send_canon_maps; +MAPS *cleanup_rcpt_canon_maps; +int cleanup_comm_canon_flags; +int cleanup_send_canon_flags; +int cleanup_rcpt_canon_flags; +MAPS *cleanup_header_checks; +MAPS *cleanup_mimehdr_checks; +MAPS *cleanup_nesthdr_checks; +MAPS *cleanup_body_checks; +MAPS *cleanup_virt_alias_maps; +ARGV *cleanup_masq_domains; +STRING_LIST *cleanup_masq_exceptions; +int cleanup_masq_flags; +MAPS *cleanup_send_bcc_maps; +MAPS *cleanup_rcpt_bcc_maps; + + /* + * Character filters. + */ +VSTRING *cleanup_reject_chars; +VSTRING *cleanup_strip_chars; + + /* + * Address extension propagation restrictions. + */ +int cleanup_ext_prop_mask; + + /* + * Milter support. + */ +MILTERS *cleanup_milters; + + /* + * From: header format. + */ +int hfrom_format_code; + +/* cleanup_all - callback for the runtime error handler */ + +void cleanup_all(void) +{ + cleanup_sig(0); +} + +/* cleanup_sig - callback for the SIGTERM handler */ + +void cleanup_sig(int sig) +{ + + /* + * msg_fatal() is safe against calling itself recursively, but signals + * need extra safety. + * + * XXX While running as a signal handler, can't ask the memory manager to + * release VSTRING storage. + */ + if (signal(SIGTERM, SIG_IGN) != SIG_IGN) { + if (cleanup_trace_path) { + (void) REMOVE(vstring_str(cleanup_trace_path)); + cleanup_trace_path = 0; + } + if (cleanup_path) { + (void) REMOVE(cleanup_path); + cleanup_path = 0; + } + if (sig) + _exit(sig); + } +} + +/* cleanup_pre_jail - initialize before entering the chroot jail */ + +void cleanup_pre_jail(char *unused_name, char **unused_argv) +{ + static const NAME_MASK send_canon_class_table[] = { + CANON_CLASS_ENV_FROM, CLEANUP_CANON_FLAG_ENV_FROM, + CANON_CLASS_HDR_FROM, CLEANUP_CANON_FLAG_HDR_FROM, + 0, + }; + static const NAME_MASK rcpt_canon_class_table[] = { + CANON_CLASS_ENV_RCPT, CLEANUP_CANON_FLAG_ENV_RCPT, + CANON_CLASS_HDR_RCPT, CLEANUP_CANON_FLAG_HDR_RCPT, + 0, + }; + static const NAME_MASK canon_class_table[] = { + CANON_CLASS_ENV_FROM, CLEANUP_CANON_FLAG_ENV_FROM, + CANON_CLASS_ENV_RCPT, CLEANUP_CANON_FLAG_ENV_RCPT, + CANON_CLASS_HDR_FROM, CLEANUP_CANON_FLAG_HDR_FROM, + CANON_CLASS_HDR_RCPT, CLEANUP_CANON_FLAG_HDR_RCPT, + 0, + }; + static const NAME_MASK masq_class_table[] = { + MASQ_CLASS_ENV_FROM, CLEANUP_MASQ_FLAG_ENV_FROM, + MASQ_CLASS_ENV_RCPT, CLEANUP_MASQ_FLAG_ENV_RCPT, + MASQ_CLASS_HDR_FROM, CLEANUP_MASQ_FLAG_HDR_FROM, + MASQ_CLASS_HDR_RCPT, CLEANUP_MASQ_FLAG_HDR_RCPT, + 0, + }; + + if (*var_canonical_maps) + cleanup_comm_canon_maps = + maps_create(VAR_CANONICAL_MAPS, var_canonical_maps, + DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX + | DICT_FLAG_UTF8_REQUEST); + if (*var_send_canon_maps) + cleanup_send_canon_maps = + maps_create(VAR_SEND_CANON_MAPS, var_send_canon_maps, + DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX + | DICT_FLAG_UTF8_REQUEST); + if (*var_rcpt_canon_maps) + cleanup_rcpt_canon_maps = + maps_create(VAR_RCPT_CANON_MAPS, var_rcpt_canon_maps, + DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX + | DICT_FLAG_UTF8_REQUEST); + if (*var_virt_alias_maps) + cleanup_virt_alias_maps = maps_create(VAR_VIRT_ALIAS_MAPS, + var_virt_alias_maps, + DICT_FLAG_LOCK + | DICT_FLAG_FOLD_FIX + | DICT_FLAG_UTF8_REQUEST); + if (*var_canon_classes) + cleanup_comm_canon_flags = + name_mask(VAR_CANON_CLASSES, canon_class_table, + var_canon_classes); + if (*var_send_canon_classes) + cleanup_send_canon_flags = + name_mask(VAR_CANON_CLASSES, send_canon_class_table, + var_send_canon_classes); + if (*var_rcpt_canon_classes) + cleanup_rcpt_canon_flags = + name_mask(VAR_CANON_CLASSES, rcpt_canon_class_table, + var_rcpt_canon_classes); + if (*var_masq_domains) + cleanup_masq_domains = argv_split(var_masq_domains, CHARS_COMMA_SP); + if (*var_header_checks) + cleanup_header_checks = + maps_create(VAR_HEADER_CHECKS, var_header_checks, DICT_FLAG_LOCK); + if (*var_mimehdr_checks) + cleanup_mimehdr_checks = + maps_create(VAR_MIMEHDR_CHECKS, var_mimehdr_checks, DICT_FLAG_LOCK); + if (*var_nesthdr_checks) + cleanup_nesthdr_checks = + maps_create(VAR_NESTHDR_CHECKS, var_nesthdr_checks, DICT_FLAG_LOCK); + if (*var_body_checks) + cleanup_body_checks = + maps_create(VAR_BODY_CHECKS, var_body_checks, DICT_FLAG_LOCK); + if (*var_masq_exceptions) + cleanup_masq_exceptions = + string_list_init(VAR_MASQ_EXCEPTIONS, MATCH_FLAG_RETURN, + var_masq_exceptions); + if (*var_masq_classes) + cleanup_masq_flags = name_mask(VAR_MASQ_CLASSES, masq_class_table, + var_masq_classes); + if (*var_send_bcc_maps) + cleanup_send_bcc_maps = + maps_create(VAR_SEND_BCC_MAPS, var_send_bcc_maps, + DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX + | DICT_FLAG_UTF8_REQUEST); + if (*var_rcpt_bcc_maps) + cleanup_rcpt_bcc_maps = + maps_create(VAR_RCPT_BCC_MAPS, var_rcpt_bcc_maps, + DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX + | DICT_FLAG_UTF8_REQUEST); + if (*var_cleanup_milters) + cleanup_milters = milter_create(var_cleanup_milters, + var_milt_conn_time, + var_milt_cmd_time, + var_milt_msg_time, + var_milt_protocol, + var_milt_def_action, + var_milt_conn_macros, + var_milt_helo_macros, + var_milt_mail_macros, + var_milt_rcpt_macros, + var_milt_data_macros, + var_milt_eoh_macros, + var_milt_eod_macros, + var_milt_unk_macros, + var_milt_macro_deflts); + if (*var_milt_head_checks) + cleanup_milter_header_checks_init(); + + flush_init(); +} + +/* cleanup_post_jail - initialize after entering the chroot jail */ + +void cleanup_post_jail(char *unused_name, char **unused_argv) +{ + static const NAME_CODE hfrom_format_table[] = { + HFROM_FORMAT_NAME_STD, HFROM_FORMAT_CODE_STD, + HFROM_FORMAT_NAME_OBS, HFROM_FORMAT_CODE_OBS, + 0, -1, + }; + + /* + * Optionally set the file size resource limit. XXX This limits the + * message content to somewhat less than requested, because the total + * queue file size also includes envelope information. Unless people set + * really low limit, the difference is going to matter only when a queue + * file has lots of recipients. + */ + if (ENFORCING_SIZE_LIMIT(var_message_limit)) + set_file_limit((off_t) var_message_limit); + + /* + * Control how unmatched extensions are propagated. + */ + cleanup_ext_prop_mask = + ext_prop_mask(VAR_PROP_EXTENSION, var_prop_extension); + + /* + * Setup the filters for characters that should be rejected, and for + * characters that should be removed. + */ + if (*var_msg_reject_chars) { + cleanup_reject_chars = vstring_alloc(strlen(var_msg_reject_chars)); + unescape(cleanup_reject_chars, var_msg_reject_chars); + } + if (*var_msg_strip_chars) { + cleanup_strip_chars = vstring_alloc(strlen(var_msg_strip_chars)); + unescape(cleanup_strip_chars, var_msg_strip_chars); + } + + /* + * From: header formatting. + */ + if ((hfrom_format_code = name_code(hfrom_format_table, + NAME_CODE_FLAG_NONE, var_hfrom_format)) < 0) + msg_fatal("invalid setting: %s = %s", + VAR_HFROM_FORMAT, var_hfrom_format); +} diff --git a/src/cleanup/cleanup_map11.c b/src/cleanup/cleanup_map11.c new file mode 100644 index 0000000..4934aac --- /dev/null +++ b/src/cleanup/cleanup_map11.c @@ -0,0 +1,183 @@ +/*++ +/* NAME +/* cleanup_map11 3 +/* SUMMARY +/* one-to-one mapping +/* SYNOPSIS +/* #include <cleanup.h> +/* +/* int cleanup_map11_external(state, addr, maps, propagate) +/* CLEANUP_STATE *state; +/* VSTRING *addr; +/* MAPS *maps; +/* int propagate; +/* +/* int cleanup_map11_internal(state, addr, maps, propagate) +/* CLEANUP_STATE *state; +/* VSTRING *addr; +/* MAPS *maps; +/* int propagate; +/* +/* int cleanup_map11_tree(state, tree, maps, propagate) +/* CLEANUP_STATE *state; +/* TOK822 *tree; +/* MAPS *maps; +/* int propagate; +/* DESCRIPTION +/* This module performs one-to-one map lookups. +/* +/* If an address has a mapping, the lookup result is +/* subjected to another iteration of rewriting and mapping. +/* Recursion continues until an address maps onto itself, +/* or until an unreasonable recursion level is reached. +/* An unmatched address extension is propagated when +/* \fIpropagate\fR is non-zero. +/* These functions return non-zero when the address was changed. +/* +/* cleanup_map11_external() looks up the external (quoted) string +/* form of an address in the maps specified via the \fImaps\fR argument. +/* +/* cleanup_map11_internal() is a wrapper around the +/* cleanup_map11_external() routine that transforms from +/* internal (quoted) string form to external form and back. +/* +/* cleanup_map11_tree() is a wrapper around the +/* cleanup_map11_external() routine that transforms from +/* internal parse tree form to external form and back. +/* DIAGNOSTICS +/* Recoverable errors: the global \fIcleanup_errs\fR flag is updated. +/* SEE ALSO +/* mail_addr_find(3) address lookups +/* mail_addr_map(3) address mappings +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <string.h> + +/* Utility library. */ + +#include <msg.h> +#include <vstring.h> +#include <dict.h> +#include <mymalloc.h> +#include <stringops.h> + +/* Global library. */ + +#include <cleanup_user.h> +#include <mail_addr_map.h> +#include <quote_822_local.h> + +/* Application-specific. */ + +#include "cleanup.h" + +#define STR vstring_str +#define MAX_RECURSION 10 + +/* cleanup_map11_external - one-to-one table lookups */ + +int cleanup_map11_external(CLEANUP_STATE *state, VSTRING *addr, + MAPS *maps, int propagate) +{ + int count; + int expand_to_self; + ARGV *new_addr; + char *saved_addr; + int did_rewrite = 0; + + /* + * Produce sensible output even in the face of a recoverable error. This + * simplifies error recovery considerably because we can do delayed error + * checking in one place, instead of having error handling code all over + * the place. + */ + for (count = 0; count < MAX_RECURSION; count++) { + if ((new_addr = mail_addr_map_opt(maps, STR(addr), propagate, + MA_FORM_EXTERNAL, MA_FORM_EXTERNAL, + MA_FORM_EXTERNAL)) != 0) { + if (new_addr->argc > 1) + msg_warn("%s: multi-valued %s entry for %s", + state->queue_id, maps->title, STR(addr)); + saved_addr = mystrdup(STR(addr)); + did_rewrite |= strcmp(new_addr->argv[0], STR(addr)); + vstring_strcpy(addr, new_addr->argv[0]); + expand_to_self = !strcasecmp_utf8(saved_addr, STR(addr)); + myfree(saved_addr); + argv_free(new_addr); + if (expand_to_self) + return (did_rewrite); + } else if (maps->error != 0) { + msg_warn("%s: %s map lookup problem for %s -- " + "message not accepted, try again later", + state->queue_id, maps->title, STR(addr)); + state->errs |= CLEANUP_STAT_WRITE; + return (did_rewrite); + } else { + return (did_rewrite); + } + } + msg_warn("%s: unreasonable %s map nesting for %s -- " + "message not accepted, try again later", + state->queue_id, maps->title, STR(addr)); + return (did_rewrite); +} + +/* cleanup_map11_tree - rewrite address node */ + +int cleanup_map11_tree(CLEANUP_STATE *state, TOK822 *tree, + MAPS *maps, int propagate) +{ + VSTRING *temp = vstring_alloc(100); + int did_rewrite; + + /* + * Produce sensible output even in the face of a recoverable error. This + * simplifies error recovery considerably because we can do delayed error + * checking in one place, instead of having error handling code all over + * the place. + */ + tok822_externalize(temp, tree->head, TOK822_STR_DEFL); + did_rewrite = cleanup_map11_external(state, temp, maps, propagate); + tok822_free_tree(tree->head); + tree->head = tok822_scan(STR(temp), &tree->tail); + vstring_free(temp); + return (did_rewrite); +} + +/* cleanup_map11_internal - rewrite address internal form */ + +int cleanup_map11_internal(CLEANUP_STATE *state, VSTRING *addr, + MAPS *maps, int propagate) +{ + VSTRING *temp = vstring_alloc(100); + int did_rewrite; + + /* + * Produce sensible output even in the face of a recoverable error. This + * simplifies error recovery considerably because we can do delayed error + * checking in one place, instead of having error handling code all over + * the place. + */ + quote_822_local(temp, STR(addr)); + did_rewrite = cleanup_map11_external(state, temp, maps, propagate); + unquote_822_local(addr, STR(temp)); + vstring_free(temp); + return (did_rewrite); +} diff --git a/src/cleanup/cleanup_map1n.c b/src/cleanup/cleanup_map1n.c new file mode 100644 index 0000000..9cecfcb --- /dev/null +++ b/src/cleanup/cleanup_map1n.c @@ -0,0 +1,182 @@ +/*++ +/* NAME +/* cleanup_map1n 3 +/* SUMMARY +/* one-to-many address mapping +/* SYNOPSIS +/* #include <cleanup.h> +/* +/* ARGV *cleanup_map1n_internal(state, addr, maps, propagate) +/* CLEANUP_STATE *state; +/* const char *addr; +/* MAPS *maps; +/* int propagate; +/* DESCRIPTION +/* This module implements one-to-many table mapping via table lookup. +/* Table lookups are done with quoted (externalized) address forms. +/* The process is recursive. The recursion terminates when the +/* left-hand side appears in its own expansion. +/* +/* cleanup_map1n_internal() is the interface for addresses in +/* internal (unquoted) form. +/* DIAGNOSTICS +/* When the maximal expansion or recursion limit is reached, +/* the alias is not expanded and the CLEANUP_STAT_DEFER error +/* is raised with reason "4.6.0 Alias expansion error". +/* +/* When table lookup fails, the alias is not expanded and the +/* CLEANUP_STAT_WRITE error is raised with reason "4.6.0 Alias +/* expansion error". +/* SEE ALSO +/* mail_addr_map(3) address mappings +/* mail_addr_find(3) address lookups +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <string.h> + +/* Utility library. */ + +#include <mymalloc.h> +#include <msg.h> +#include <argv.h> +#include <vstring.h> +#include <dict.h> +#include <stringops.h> + +/* Global library. */ + +#include <mail_params.h> +#include <mail_addr_map.h> +#include <cleanup_user.h> +#include <quote_822_local.h> +#include <been_here.h> + +/* Application-specific. */ + +#include "cleanup.h" + +/* cleanup_map1n_internal - one-to-many table lookups */ + +ARGV *cleanup_map1n_internal(CLEANUP_STATE *state, const char *addr, + MAPS *maps, int propagate) +{ + ARGV *argv; + ARGV *lookup; + int count; + int i; + int arg; + BH_TABLE *been_here; + char *saved_lhs; + + /* + * Initialize. + */ + argv = argv_alloc(1); + argv_add(argv, addr, ARGV_END); + argv_terminate(argv); + been_here = been_here_init(0, BH_FLAG_FOLD); + + /* + * Rewrite the address vector in place. With each map lookup result, + * split it into separate addresses, then rewrite and flatten each + * address, and repeat the process. Beware: argv is being changed, so we + * must index the array explicitly, instead of running along it with a + * pointer. + */ +#define UPDATE(ptr,new) do { \ + if (ptr) myfree(ptr); ptr = mystrdup(new); \ + } while (0) +#define STR vstring_str +#define RETURN(x) do { \ + been_here_free(been_here); return (x); \ + } while (0) +#define UNEXPAND(argv, addr) do { \ + argv_truncate((argv), 0); argv_add((argv), (addr), (char *) 0); \ + } while (0) + + for (arg = 0; arg < argv->argc; arg++) { + if (argv->argc > var_virt_expan_limit) { + msg_warn("%s: unreasonable %s map expansion size for %s -- " + "message not accepted, try again later", + state->queue_id, maps->title, addr); + state->errs |= CLEANUP_STAT_DEFER; + UPDATE(state->reason, "4.6.0 Alias expansion error"); + UNEXPAND(argv, addr); + RETURN(argv); + } + for (count = 0; /* void */ ; count++) { + + /* + * Don't expand an address that already expanded into itself. + */ + if (been_here_check_fixed(been_here, argv->argv[arg]) != 0) + break; + if (count >= var_virt_recur_limit) { + msg_warn("%s: unreasonable %s map nesting for %s -- " + "message not accepted, try again later", + state->queue_id, maps->title, addr); + state->errs |= CLEANUP_STAT_DEFER; + UPDATE(state->reason, "4.6.0 Alias expansion error"); + UNEXPAND(argv, addr); + RETURN(argv); + } + if ((lookup = mail_addr_map_internal(maps, argv->argv[arg], + propagate)) != 0) { + saved_lhs = mystrdup(argv->argv[arg]); + for (i = 0; i < lookup->argc; i++) { + if (strlen(lookup->argv[i]) > var_virt_addrlen_limit) { + msg_warn("%s: unreasonable %s result %.300s... -- " + "message not accepted, try again later", + state->queue_id, maps->title, lookup->argv[i]); + state->errs |= CLEANUP_STAT_DEFER; + UPDATE(state->reason, "4.6.0 Alias expansion error"); + UNEXPAND(argv, addr); + RETURN(argv); + } + if (i == 0) { + UPDATE(argv->argv[arg], lookup->argv[i]); + } else { + argv_add(argv, lookup->argv[i], ARGV_END); + argv_terminate(argv); + } + + /* + * Allow an address to expand into itself once. + */ + if (strcasecmp_utf8(saved_lhs, lookup->argv[i]) == 0) + been_here_fixed(been_here, saved_lhs); + } + myfree(saved_lhs); + argv_free(lookup); + } else if (maps->error != 0) { + msg_warn("%s: %s map lookup problem for %s -- " + "message not accepted, try again later", + state->queue_id, maps->title, addr); + state->errs |= CLEANUP_STAT_WRITE; + UPDATE(state->reason, "4.6.0 Alias expansion error"); + UNEXPAND(argv, addr); + RETURN(argv); + } else { + break; + } + } + } + RETURN(argv); +} diff --git a/src/cleanup/cleanup_masq.ref b/src/cleanup/cleanup_masq.ref new file mode 100644 index 0000000..6838894 --- /dev/null +++ b/src/cleanup/cleanup_masq.ref @@ -0,0 +1,66 @@ +---------- +exceptions: +masq_list: a.b.c,b.c +address: xxx@aa.a.b.c +result: xxx@a.b.c +errs: 0 +---------- +exceptions: +masq_list: A.B.C,B.C +address: xxx@aa.a.b.c +result: xxx@a.b.c +errs: 0 +---------- +exceptions: +masq_list: a.b.c,b.c +address: xxx@AA.A.B.C +result: xxx@a.b.c +errs: 0 +---------- +exceptions: xxx +masq_list: a.b.c,b.c +address: xxx@aa.a.b.c +result: xxx@aa.a.b.c +errs: 0 +---------- +exceptions: yyy +masq_list: a.b.c,b.c +address: xxx@aa.a.b.c +result: xxx@a.b.c +errs: 0 +---------- +exceptions: +masq_list: !a.b.c,b.c +address: xxx@aa.a.b.c +result: xxx@aa.a.b.c +errs: 0 +---------- +exceptions: +masq_list: a.b.c,b.c +address: xxx@a.b.c +result: xxx@a.b.c +errs: 0 +---------- +exceptions: +masq_list: !a.b.c,b.c +address: xxx@a.b.c +result: xxx@a.b.c +errs: 0 +---------- +exceptions: +masq_list: a.b.c,b.c +address: xxx@aaa.b.c +result: xxx@b.c +errs: 0 +---------- +exceptions: +masq_list: a.b.c,b.c +address: xxx@b.c +result: xxx@b.c +errs: 0 +---------- +exceptions: fail:whatever +masq_list: xy +address: xxx@b.c +result: xxx@b.c +errs: 2 diff --git a/src/cleanup/cleanup_masquerade.c b/src/cleanup/cleanup_masquerade.c new file mode 100644 index 0000000..4fc3a13 --- /dev/null +++ b/src/cleanup/cleanup_masquerade.c @@ -0,0 +1,239 @@ +/*++ +/* NAME +/* cleanup_masquerade 3 +/* SUMMARY +/* address masquerading +/* SYNOPSIS +/* #include <cleanup.h> +/* +/* int cleanup_masquerade_external(addr, masq_domains) +/* VSTRING *addr; +/* ARGV *masq_domains; +/* +/* int cleanup_masquerade_internal(addr, masq_domains) +/* VSTRING *addr; +/* ARGV *masq_domains; +/* +/* int cleanup_masquerade_tree(tree, masq_domains) +/* TOK822 *tree; +/* ARGV *masq_domains; +/* DESCRIPTION +/* This module masquerades addresses, that is, it strips subdomains +/* below domain names that are listed in the masquerade_domains +/* configuration parameter, except for user names listed in the +/* masquerade_exceptions configuration parameter. +/* These functions return non-zero when the address was changed. +/* +/* cleanup_masquerade_external() rewrites the external (quoted) string +/* form of an address. +/* +/* cleanup_masquerade_internal() is a wrapper around the +/* cleanup_masquerade_external() routine that transforms from +/* internal (quoted) string form to external form and back. +/* +/* cleanup_masquerade_tree() is a wrapper around the +/* cleanup_masquerade_external() routine that transforms from +/* internal parse tree form to external form and back. +/* DIAGNOSTICS +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <string.h> + +/* Utility library. */ + +#include <msg.h> +#include <vstring.h> +#include <argv.h> +#include <htable.h> +#include <mymalloc.h> +#include <stringops.h> + +/* Global library. */ + +#include <mail_params.h> +#include <tok822.h> +#include <quote_822_local.h> + +/* Application-specific. */ + +#include "cleanup.h" + +#define STR vstring_str + +/* cleanup_masquerade_external - masquerade address external form */ + +int cleanup_masquerade_external(CLEANUP_STATE *state, VSTRING *addr, + ARGV *masq_domains) +{ + char *domain; + ssize_t domain_len; + char **masqp; + char *masq; + ssize_t masq_len; + char *parent; + int truncate; + int did_rewrite = 0; + + /* Stuff for excluded names. */ + char *name; + ssize_t name_len; + int excluded; + + /* + * Find the domain part. + */ + if ((domain = strrchr(STR(addr), '@')) == 0) + return (0); + name_len = domain - STR(addr); + domain = casefold(state->temp2, domain + 1); + domain_len = strlen(domain); + + /* + * Don't masquerade excluded names (regardless of domain). + */ + if (*var_masq_exceptions) { + name = mystrndup(STR(addr), name_len); + excluded = (string_list_match(cleanup_masq_exceptions, name) != 0); + myfree(name); + if (cleanup_masq_exceptions->error) { + msg_info("%s: %s map lookup problem -- " + "message not accepted, try again later", + state->queue_id, VAR_MASQ_EXCEPTIONS); + state->errs |= CLEANUP_STAT_WRITE; + } + if (excluded) + return (0); + } + + /* + * If any parent domain matches the list of masquerade domains, replace + * the domain in the address and terminate. If the domain matches a + * masquerade domain, leave it alone. Order of specification matters. + */ + for (masqp = masq_domains->argv; (masq = *masqp) != 0; masqp++) { + for (truncate = 1; *masq == '!'; masq++) + truncate = !truncate; + masq = casefold(state->temp1, masq); + masq_len = strlen(masq); + if (masq_len == 0) + continue; + if (masq_len == domain_len) { + if (strcmp(masq, domain) == 0) + break; + } else if (masq_len < domain_len) { + parent = domain + domain_len - masq_len; + if (parent[-1] == '.' && strcmp(masq, parent) == 0) { + if (truncate) { + if (msg_verbose) + msg_info("masquerade: %s -> %s", domain, masq); + vstring_truncate(addr, name_len + 1); + vstring_strcat(addr, masq); + did_rewrite = 1; + } + break; + } + } + } + return (did_rewrite); +} + +/* cleanup_masquerade_tree - masquerade address node */ + +int cleanup_masquerade_tree(CLEANUP_STATE *state, TOK822 *tree, + ARGV *masq_domains) +{ + VSTRING *temp = vstring_alloc(100); + int did_rewrite; + + tok822_externalize(temp, tree->head, TOK822_STR_DEFL); + did_rewrite = cleanup_masquerade_external(state, temp, masq_domains); + tok822_free_tree(tree->head); + tree->head = tok822_scan(STR(temp), &tree->tail); + + vstring_free(temp); + return (did_rewrite); +} + +/* cleanup_masquerade_internal - masquerade address internal form */ + +int cleanup_masquerade_internal(CLEANUP_STATE *state, VSTRING *addr, + ARGV *masq_domains) +{ + VSTRING *temp = vstring_alloc(100); + int did_rewrite; + + quote_822_local(temp, STR(addr)); + did_rewrite = cleanup_masquerade_external(state, temp, masq_domains); + unquote_822_local(addr, STR(temp)); + + vstring_free(temp); + return (did_rewrite); +} + + /* + * Code for stand-alone testing. Instead of using main.cf, specify the strip + * list and the candidate domain on the command line. Specify null arguments + * for data that should be empty. + */ +#ifdef TEST + +#include <vstream.h> + +char *var_masq_exceptions; +STRING_LIST *cleanup_masq_exceptions; + +int main(int argc, char **argv) +{ + VSTRING *addr; + ARGV *masq_domains; + CLEANUP_STATE state; + + if (argc != 4) + msg_fatal("usage: %s exceptions masquerade_list address", argv[0]); + + var_masq_exceptions = argv[1]; + cleanup_masq_exceptions = + string_list_init(VAR_MASQ_EXCEPTIONS, MATCH_FLAG_RETURN, + var_masq_exceptions); + masq_domains = argv_split(argv[2], CHARS_COMMA_SP); + addr = vstring_alloc(1); + if (strchr(argv[3], '@') == 0) + msg_fatal("address must be in user@domain form"); + vstring_strcpy(addr, argv[3]); + + vstream_printf("----------\n"); + vstream_printf("exceptions: %s\n", argv[1]); + vstream_printf("masq_list: %s\n", argv[2]); + vstream_printf("address: %s\n", argv[3]); + + state.errs = 0; + state.queue_id = "NOQUEUE"; + state.temp1 = vstring_alloc(100); + state.temp2 = vstring_alloc(100); + cleanup_masquerade_external(&state, addr, masq_domains); + + vstream_printf("result: %s\n", STR(addr)); + vstream_printf("errs: %d\n", state.errs); + vstream_fflush(VSTREAM_OUT); + + vstring_free(state.temp1); + vstring_free(state.temp2); + vstring_free(addr); + argv_free(masq_domains); + + return (0); +} + +#endif diff --git a/src/cleanup/cleanup_message.c b/src/cleanup/cleanup_message.c new file mode 100644 index 0000000..4284c46 --- /dev/null +++ b/src/cleanup/cleanup_message.c @@ -0,0 +1,1116 @@ +/*++ +/* NAME +/* cleanup_message 3 +/* SUMMARY +/* process message segment +/* SYNOPSIS +/* #include "cleanup.h" +/* +/* void cleanup_message(state, type, buf, len) +/* CLEANUP_STATE *state; +/* int type; +/* const char *buf; +/* ssize_t len; +/* DESCRIPTION +/* This module processes message content records and copies the +/* result to the queue file. It validates the input, rewrites +/* sender/recipient addresses to canonical form, inserts missing +/* message headers, and extracts information from message headers +/* to be used later when generating the extracted output segment. +/* This routine absorbs but does not emit the content to extracted +/* boundary record. +/* +/* Arguments: +/* .IP state +/* Queue file and message processing state. This state is updated +/* as records are processed and as errors happen. +/* .IP type +/* Record type. +/* .IP buf +/* Record content. +/* .IP len +/* Record content length. +/* 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 <ctype.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#ifdef STRCASECMP_IN_STRINGS_H +#include <strings.h> +#endif + +/* Utility library. */ + +#include <msg.h> +#include <vstring.h> +#include <vstream.h> +#include <argv.h> +#include <split_at.h> +#include <mymalloc.h> +#include <stringops.h> +#include <nvtable.h> + +/* Global library. */ + +#include <record.h> +#include <rec_type.h> +#include <cleanup_user.h> +#include <tok822.h> +#include <lex_822.h> +#include <header_opts.h> +#include <quote_822_local.h> +#include <mail_params.h> +#include <mail_date.h> +#include <mail_addr.h> +#include <is_header.h> +#include <ext_prop.h> +#include <mail_proto.h> +#include <mime_state.h> +#include <lex_822.h> +#include <dsn_util.h> +#include <conv_time.h> +#include <info_log_addr_form.h> + +/* Application-specific. */ + +#include "cleanup.h" + +/* cleanup_fold_header - wrap address list header */ + +static void cleanup_fold_header(CLEANUP_STATE *state, VSTRING *header_buf) +{ + char *start_line = vstring_str(header_buf); + char *end_line; + char *next_line; + char *line; + + /* + * A rewritten address list contains one address per line. The code below + * replaces newlines by spaces, to fit as many addresses on a line as + * possible (without rearranging the order of addresses). Prepending + * white space to the beginning of lines is delegated to the output + * routine. + */ + for (line = start_line; line != 0; line = next_line) { + end_line = line + strcspn(line, "\n"); + if (line > start_line) { + if (end_line - start_line < 70) { /* TAB counts as one */ + line[-1] = ' '; + } else { + start_line = line; + } + } + next_line = *end_line ? end_line + 1 : 0; + } + cleanup_out_header(state, header_buf); +} + +/* cleanup_extract_internal - save unquoted copy of extracted address */ + +static char *cleanup_extract_internal(VSTRING *buffer, TOK822 *addr) +{ + + /* + * A little routine to stash away a copy of an address that we extracted + * from a message header line. + */ + tok822_internalize(buffer, addr->head, TOK822_STR_DEFL); + return (mystrdup(vstring_str(buffer))); +} + +/* cleanup_rewrite_sender - sender address rewriting */ + +static void cleanup_rewrite_sender(CLEANUP_STATE *state, + const HEADER_OPTS *hdr_opts, + VSTRING *header_buf) +{ + TOK822 *tree; + TOK822 **addr_list; + TOK822 **tpp; + int did_rewrite = 0; + + if (msg_verbose) + msg_info("rewrite_sender: %s", hdr_opts->name); + + /* + * Parse the header line, rewrite each address found, and regenerate the + * header line. Finally, pipe the result through the header line folding + * routine. + */ + tree = tok822_parse_limit(vstring_str(header_buf) + + strlen(hdr_opts->name) + 1, + var_token_limit); + addr_list = tok822_grep(tree, TOK822_ADDR); + for (tpp = addr_list; *tpp; tpp++) { + did_rewrite |= cleanup_rewrite_tree(state->hdr_rewrite_context, *tpp); + if (state->flags & CLEANUP_FLAG_MAP_OK) { + if (cleanup_send_canon_maps + && (cleanup_send_canon_flags & CLEANUP_CANON_FLAG_HDR_FROM)) + did_rewrite |= + cleanup_map11_tree(state, *tpp, cleanup_send_canon_maps, + cleanup_ext_prop_mask & EXT_PROP_CANONICAL); + if (cleanup_comm_canon_maps + && (cleanup_comm_canon_flags & CLEANUP_CANON_FLAG_HDR_FROM)) + did_rewrite |= + cleanup_map11_tree(state, *tpp, cleanup_comm_canon_maps, + cleanup_ext_prop_mask & EXT_PROP_CANONICAL); + if (cleanup_masq_domains + && (cleanup_masq_flags & CLEANUP_MASQ_FLAG_HDR_FROM)) + did_rewrite |= + cleanup_masquerade_tree(state, *tpp, cleanup_masq_domains); + } + } + if (did_rewrite) { + vstring_truncate(header_buf, strlen(hdr_opts->name)); + vstring_strcat(header_buf, ": "); + tok822_externalize(header_buf, tree, TOK822_STR_HEAD); + } + myfree((void *) addr_list); + tok822_free_tree(tree); + if ((hdr_opts->flags & HDR_OPT_DROP) == 0) { + if (did_rewrite) + cleanup_fold_header(state, header_buf); + else + cleanup_out_header(state, header_buf); + } +} + +/* cleanup_rewrite_recip - recipient address rewriting */ + +static void cleanup_rewrite_recip(CLEANUP_STATE *state, + const HEADER_OPTS *hdr_opts, + VSTRING *header_buf) +{ + TOK822 *tree; + TOK822 **addr_list; + TOK822 **tpp; + int did_rewrite = 0; + + if (msg_verbose) + msg_info("rewrite_recip: %s", hdr_opts->name); + + /* + * Parse the header line, rewrite each address found, and regenerate the + * header line. Finally, pipe the result through the header line folding + * routine. + */ + tree = tok822_parse_limit(vstring_str(header_buf) + + strlen(hdr_opts->name) + 1, + var_token_limit); + addr_list = tok822_grep(tree, TOK822_ADDR); + for (tpp = addr_list; *tpp; tpp++) { + did_rewrite |= cleanup_rewrite_tree(state->hdr_rewrite_context, *tpp); + if (state->flags & CLEANUP_FLAG_MAP_OK) { + if (cleanup_rcpt_canon_maps + && (cleanup_rcpt_canon_flags & CLEANUP_CANON_FLAG_HDR_RCPT)) + did_rewrite |= + cleanup_map11_tree(state, *tpp, cleanup_rcpt_canon_maps, + cleanup_ext_prop_mask & EXT_PROP_CANONICAL); + if (cleanup_comm_canon_maps + && (cleanup_comm_canon_flags & CLEANUP_CANON_FLAG_HDR_RCPT)) + did_rewrite |= + cleanup_map11_tree(state, *tpp, cleanup_comm_canon_maps, + cleanup_ext_prop_mask & EXT_PROP_CANONICAL); + if (cleanup_masq_domains + && (cleanup_masq_flags & CLEANUP_MASQ_FLAG_HDR_RCPT)) + did_rewrite |= + cleanup_masquerade_tree(state, *tpp, cleanup_masq_domains); + } + } + if (did_rewrite) { + vstring_truncate(header_buf, strlen(hdr_opts->name)); + vstring_strcat(header_buf, ": "); + tok822_externalize(header_buf, tree, TOK822_STR_HEAD); + } + myfree((void *) addr_list); + tok822_free_tree(tree); + if ((hdr_opts->flags & HDR_OPT_DROP) == 0) { + if (did_rewrite) + cleanup_fold_header(state, header_buf); + else + cleanup_out_header(state, header_buf); + } +} + +/* cleanup_act_log - log action with context */ + +static void cleanup_act_log(CLEANUP_STATE *state, + const char *action, const char *class, + const char *content, const char *text) +{ + const char *attr; + + if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_ORIGIN)) == 0) + attr = "unknown"; + vstring_sprintf(state->temp1, "%s: %s: %s %.200s from %s;", + state->queue_id, action, class, content, attr); + if (state->sender) + vstring_sprintf_append(state->temp1, " from=<%s>", + info_log_addr_form_sender(state->sender)); + if (state->recip) + vstring_sprintf_append(state->temp1, " to=<%s>", + info_log_addr_form_recipient(state->recip)); + if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_PROTO_NAME)) != 0) + vstring_sprintf_append(state->temp1, " proto=%s", attr); + if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_HELO_NAME)) != 0) + vstring_sprintf_append(state->temp1, " helo=<%s>", attr); + if (text && *text) + vstring_sprintf_append(state->temp1, ": %s", text); + msg_info("%s", vstring_str(state->temp1)); +} + +#define CLEANUP_ACT_CTXT_HEADER "header" +#define CLEANUP_ACT_CTXT_BODY "body" +#define CLEANUP_ACT_CTXT_ANY "content" + +/* cleanup_act - act upon a header/body match */ + +static const char *cleanup_act(CLEANUP_STATE *state, char *context, + const char *buf, const char *value, + const char *map_class) +{ + const char *optional_text = value + strcspn(value, " \t"); + int command_len = optional_text - value; + +#ifdef DELAY_ACTION + int defer_delay; + +#endif + + while (*optional_text && ISSPACE(*optional_text)) + optional_text++; + +#define STREQUAL(x,y,l) (strncasecmp((x), (y), (l)) == 0 && (y)[l] == 0) +#define CLEANUP_ACT_DROP 0 + + /* + * CLEANUP_STAT_CONT and CLEANUP_STAT_DEFER both update the reason + * attribute, but CLEANUP_STAT_DEFER takes precedence. It terminates + * queue record processing, and prevents bounces from being sent. + */ + if (STREQUAL(value, "REJECT", command_len)) { + const CLEANUP_STAT_DETAIL *detail; + + if (state->reason) + myfree(state->reason); + if (*optional_text) { + state->reason = dsn_prepend("5.7.1", optional_text); + if (*state->reason != '4' && *state->reason != '5') { + msg_warn("bad DSN action in %s -- need 4.x.x or 5.x.x", + optional_text); + *state->reason = '4'; + } + } else { + detail = cleanup_stat_detail(CLEANUP_STAT_CONT); + state->reason = dsn_prepend(detail->dsn, detail->text); + } + if (*state->reason == '4') + state->errs |= CLEANUP_STAT_DEFER; + else + state->errs |= CLEANUP_STAT_CONT; + state->flags &= ~CLEANUP_FLAG_FILTER_ALL; + cleanup_act_log(state, "reject", context, buf, state->reason); + return (buf); + } + if (STREQUAL(value, "WARN", command_len)) { + cleanup_act_log(state, "warning", context, buf, optional_text); + return (buf); + } + if (STREQUAL(value, "INFO", command_len)) { + cleanup_act_log(state, "info", context, buf, optional_text); + return (buf); + } + if (STREQUAL(value, "FILTER", command_len)) { + if (*optional_text == 0) { + msg_warn("missing FILTER command argument in %s map", map_class); + } else if (strchr(optional_text, ':') == 0) { + msg_warn("bad FILTER command %s in %s -- " + "need transport:destination", + optional_text, map_class); + } else { + if (state->filter) + myfree(state->filter); + state->filter = mystrdup(optional_text); + cleanup_act_log(state, "filter", context, buf, optional_text); + } + return (buf); + } + if (STREQUAL(value, "PASS", command_len)) { + cleanup_act_log(state, "pass", context, buf, optional_text); + state->flags &= ~CLEANUP_FLAG_FILTER_ALL; + return (buf); + } + if (STREQUAL(value, "DISCARD", command_len)) { + cleanup_act_log(state, "discard", context, buf, optional_text); + state->flags |= CLEANUP_FLAG_DISCARD; + state->flags &= ~CLEANUP_FLAG_FILTER_ALL; + return (buf); + } + if (STREQUAL(value, "HOLD", command_len)) { + if ((state->flags & (CLEANUP_FLAG_HOLD | CLEANUP_FLAG_DISCARD)) == 0) { + cleanup_act_log(state, "hold", context, buf, optional_text); + state->flags |= CLEANUP_FLAG_HOLD; + } + return (buf); + } + + /* + * The DELAY feature is disabled because it has too many problems. 1) It + * does not work on some remote file systems; 2) mail will be delivered + * anyway with "sendmail -q" etc.; 3) while the mail is queued it bogs + * down the deferred queue scan with huge amounts of useless disk I/O + * operations. + */ +#ifdef DELAY_ACTION + if (STREQUAL(value, "DELAY", command_len)) { + if ((state->flags & (CLEANUP_FLAG_HOLD | CLEANUP_FLAG_DISCARD)) == 0) { + if (*optional_text == 0) { + msg_warn("missing DELAY argument in %s map", map_class); + } else if (conv_time(optional_text, &defer_delay, 's') == 0) { + msg_warn("ignoring bad DELAY argument %s in %s map", + optional_text, map_class); + } else { + cleanup_act_log(state, "delay", context, buf, optional_text); + state->defer_delay = defer_delay; + } + } + return (buf); + } +#endif + if (STREQUAL(value, "PREPEND", command_len)) { + if (*optional_text == 0) { + msg_warn("PREPEND action without text in %s map", map_class); + } else if (strcmp(context, CLEANUP_ACT_CTXT_HEADER) == 0) { + if (!is_header(optional_text)) { + msg_warn("bad PREPEND header text \"%s\" in %s map -- " + "need \"headername: headervalue\"", + optional_text, map_class); + } + + /* + * By design, cleanup_out_header() may modify content. Play safe + * and prepare for future developments. + */ + else { + VSTRING *temp; + + cleanup_act_log(state, "prepend", context, buf, optional_text); + temp = vstring_strcpy(vstring_alloc(strlen(optional_text)), + optional_text); + cleanup_out_header(state, temp); + vstring_free(temp); + } + } else { + cleanup_act_log(state, "prepend", context, buf, optional_text); + cleanup_out_string(state, REC_TYPE_NORM, optional_text); + } + return (buf); + } + if (STREQUAL(value, "REPLACE", command_len)) { + if (*optional_text == 0) { + msg_warn("REPLACE action without text in %s map", map_class); + return (buf); + } else if (strcmp(context, CLEANUP_ACT_CTXT_HEADER) == 0 + && !is_header(optional_text)) { + msg_warn("bad REPLACE header text \"%s\" in %s map -- " + "need \"headername: headervalue\"", + optional_text, map_class); + return (buf); + } else { + cleanup_act_log(state, "replace", context, buf, optional_text); + return (mystrdup(optional_text)); + } + } + if (STREQUAL(value, "REDIRECT", command_len)) { + if (strchr(optional_text, '@') == 0) { + msg_warn("bad REDIRECT target \"%s\" in %s map -- " + "need user@domain", + optional_text, map_class); + } else { + if (state->redirect) + myfree(state->redirect); + state->redirect = mystrdup(optional_text); + cleanup_act_log(state, "redirect", context, buf, optional_text); + state->flags &= ~CLEANUP_FLAG_FILTER_ALL; + } + return (buf); + } + if (STREQUAL(value, "BCC", command_len)) { + if (strchr(optional_text, '@') == 0) { + msg_warn("bad BCC address \"%s\" in %s map -- " + "need user@domain", + optional_text, map_class); + } else { + if (state->hbc_rcpt == 0) + state->hbc_rcpt = argv_alloc(1); + argv_add(state->hbc_rcpt, optional_text, (char *) 0); + cleanup_act_log(state, "bcc", context, buf, optional_text); + } + return (buf); + } + if (STREQUAL(value, "STRIP", command_len)) { + cleanup_act_log(state, "strip", context, buf, optional_text); + return (CLEANUP_ACT_DROP); + } + /* Allow and ignore optional text after the action. */ + + if (STREQUAL(value, "IGNORE", command_len)) + return (CLEANUP_ACT_DROP); + + if (STREQUAL(value, "DUNNO", command_len)) /* preferred */ + return (buf); + + if (STREQUAL(value, "OK", command_len)) /* compat */ + return (buf); + + msg_warn("unknown command in %s map: %s", map_class, value); + return (buf); +} + +/* cleanup_header_callback - process one complete header line */ + +static void cleanup_header_callback(void *context, int header_class, + const HEADER_OPTS *hdr_opts, + VSTRING *header_buf, + off_t unused_offset) +{ + CLEANUP_STATE *state = (CLEANUP_STATE *) context; + const char *myname = "cleanup_header_callback"; + char *hdrval; + struct code_map { + const char *name; + const char *encoding; + }; + static struct code_map code_map[] = { /* RFC 2045 */ + "7bit", MAIL_ATTR_ENC_7BIT, + "8bit", MAIL_ATTR_ENC_8BIT, + "binary", MAIL_ATTR_ENC_8BIT, /* XXX Violation */ + "quoted-printable", MAIL_ATTR_ENC_7BIT, + "base64", MAIL_ATTR_ENC_7BIT, + 0, + }; + struct code_map *cmp; + MAPS *checks; + const char *map_class; + + if (msg_verbose) + msg_info("%s: '%.200s'", myname, vstring_str(header_buf)); + + /* + * Crude header filtering. This stops malware that isn't sophisticated + * enough to use fancy header encodings. + */ +#define CHECK(class, maps, var_name) \ + (header_class == class && (map_class = var_name, checks = maps) != 0) + + if (hdr_opts && (hdr_opts->flags & HDR_OPT_MIME)) + header_class = MIME_HDR_MULTIPART; + + /* Update the Received: header count before maybe dropping headers below. */ + if (hdr_opts && hdr_opts->type == HDR_RECEIVED) + state->hop_count += 1; + + if ((state->flags & CLEANUP_FLAG_FILTER) + && (CHECK(MIME_HDR_PRIMARY, cleanup_header_checks, VAR_HEADER_CHECKS) + || CHECK(MIME_HDR_MULTIPART, cleanup_mimehdr_checks, VAR_MIMEHDR_CHECKS) + || CHECK(MIME_HDR_NESTED, cleanup_nesthdr_checks, VAR_NESTHDR_CHECKS))) { + char *header = vstring_str(header_buf); + const char *value; + + if ((value = maps_find(checks, header, 0)) != 0) { + const char *result; + + if ((result = cleanup_act(state, CLEANUP_ACT_CTXT_HEADER, + header, value, map_class)) + == CLEANUP_ACT_DROP) { + return; + } else if (result != header) { + vstring_strcpy(header_buf, result); + hdr_opts = header_opts_find(result); + myfree((void *) result); + } + } else if (checks->error) { + msg_warn("%s: %s map lookup problem -- " + "message not accepted, try again later", + state->queue_id, checks->title); + state->errs |= CLEANUP_STAT_WRITE; + } + } + + /* + * If this is an "unknown" header, just copy it to the output without + * even bothering to fold long lines. cleanup_out() will split long + * headers that do not fit a REC_TYPE_NORM record. + */ + if (hdr_opts == 0) { + cleanup_out_header(state, header_buf); + return; + } + + /* + * Allow 8-bit type info to override 7-bit type info. XXX Should reuse + * the effort that went into MIME header parsing. + */ + hdrval = vstring_str(header_buf) + strlen(hdr_opts->name) + 1; + while (ISSPACE(*hdrval)) + hdrval++; + /* trimblanks(hdrval, 0)[0] = 0; */ + if (var_auto_8bit_enc_hdr + && hdr_opts->type == HDR_CONTENT_TRANSFER_ENCODING) { + for (cmp = code_map; cmp->name != 0; cmp++) { + if (strcasecmp(hdrval, cmp->name) == 0) { + if (strcasecmp(cmp->encoding, MAIL_ATTR_ENC_8BIT) == 0) + nvtable_update(state->attr, MAIL_ATTR_ENCODING, + cmp->encoding); + break; + } + } + } + + /* + * Copy attachment etc. header blocks without further inspection. + */ + if (header_class != MIME_HDR_PRIMARY) { + cleanup_out_header(state, header_buf); + return; + } + + /* + * Known header. Remember that we have seen at least one. Find out what + * we should do with this header: delete, count, rewrite. Note that we + * should examine headers even when they will be deleted from the output, + * because the addresses in those headers might be needed elsewhere. + * + * XXX 2821: Return-path breakage. + * + * RFC 821 specifies: When the receiver-SMTP makes the "final delivery" of a + * message it inserts at the beginning of the mail data a return path + * line. The return path line preserves the information in the + * <reverse-path> from the MAIL command. Here, final delivery means the + * message leaves the SMTP world. Normally, this would mean it has been + * delivered to the destination user, but in some cases it may be further + * processed and transmitted by another mail system. + * + * And that is what Postfix implements. Delivery agents prepend + * Return-Path:. In order to avoid cluttering up the message with + * possibly inconsistent Return-Path: information (the sender can change + * as the result of mail forwarding or mailing list delivery), Postfix + * removes any existing Return-Path: headers. + * + * RFC 2821 Section 4.4 specifies: A message-originating SMTP system + * SHOULD NOT send a message that already contains a Return-path header. + * SMTP servers performing a relay function MUST NOT inspect the message + * data, and especially not to the extent needed to determine if + * Return-path headers are present. SMTP servers making final delivery + * MAY remove Return-path headers before adding their own. + */ + else { + state->headers_seen |= (1 << hdr_opts->type); + if (hdr_opts->type == HDR_MESSAGE_ID) + msg_info("%s: message-id=%s", state->queue_id, hdrval); + if (hdr_opts->type == HDR_RESENT_MESSAGE_ID) + msg_info("%s: resent-message-id=%s", state->queue_id, hdrval); + if (hdr_opts->type == HDR_RECEIVED) { + if (state->hop_count >= var_hopcount_limit) { + msg_warn("%s: message rejected: hopcount exceeded", + state->queue_id); + state->errs |= CLEANUP_STAT_HOPS; + } + /* Save our Received: header after maybe updating headers above. */ + if (state->hop_count == 1) + argv_add(state->auto_hdrs, vstring_str(header_buf), ARGV_END); + } + if (CLEANUP_OUT_OK(state)) { + if (hdr_opts->flags & HDR_OPT_RR) + state->resent = "Resent-"; + if ((hdr_opts->flags & HDR_OPT_SENDER) + && state->hdr_rewrite_context) { + cleanup_rewrite_sender(state, hdr_opts, header_buf); + } else if ((hdr_opts->flags & HDR_OPT_RECIP) + && state->hdr_rewrite_context) { + cleanup_rewrite_recip(state, hdr_opts, header_buf); + } else if ((hdr_opts->flags & HDR_OPT_DROP) == 0) { + cleanup_out_header(state, header_buf); + } + } + } +} + +/* cleanup_header_done_callback - insert missing message headers */ + +static void cleanup_header_done_callback(void *context) +{ + const char *myname = "cleanup_header_done_callback"; + CLEANUP_STATE *state = (CLEANUP_STATE *) context; + char time_stamp[1024]; /* XXX locale dependent? */ + struct tm *tp; + TOK822 *token; + TOK822 *dummy_token; + time_t tv; + + /* + * XXX Workaround: when we reach the end of headers, mime_state_update() + * may execute up to three call-backs before returning to the caller: + * head_out(), head_end(), and body_out() or body_end(). As long as + * call-backs don't return a result, each call-back has to check for + * itself if the previous call-back experienced a problem. + */ + if (CLEANUP_OUT_OK(state) == 0) + return; + + /* + * Future proofing: the Milter client's header suppression algorithm + * assumes that the MTA prepends its own Received: header. This + * assupmtion may be violated after some source-code update. The + * following check ensures consistency, at least for local submission. + */ + if (state->hop_count < 1) { + msg_warn("%s: message rejected: no Received: header", + state->queue_id); + state->errs |= CLEANUP_STAT_BAD; + return; + } + + /* + * Add a missing (Resent-)Message-Id: header. The message ID gives the + * time in GMT units, plus the local queue ID. + * + * XXX Message-Id is not a required message header (RFC 822 and RFC 2822). + * + * XXX It is the queue ID non-inode bits that prevent messages from getting + * the same Message-Id within the same second. + * + * XXX An arbitrary amount of time may pass between the start of the mail + * transaction and the creation of a queue file. Since we guarantee queue + * ID uniqueness only within a second, we must ensure that the time in + * the message ID matches the queue ID creation time, as long as we use + * the queue ID in the message ID. + * + * XXX We log a dummy name=value record so that we (hopefully) don't break + * compatibility with existing logfile analyzers, and so that we don't + * complicate future code that wants to log more name=value attributes. + */ + if ((state->hdr_rewrite_context || var_always_add_hdrs) + && (state->headers_seen & (1 << (state->resent[0] ? + HDR_RESENT_MESSAGE_ID : HDR_MESSAGE_ID))) == 0) { + if (var_long_queue_ids) { + vstring_sprintf(state->temp1, "%s@%s", + state->queue_id, var_myhostname); + } else { + tv = state->handle->ctime.tv_sec; + tp = gmtime(&tv); + strftime(time_stamp, sizeof(time_stamp), "%Y%m%d%H%M%S", tp); + vstring_sprintf(state->temp1, "%s.%s@%s", + time_stamp, state->queue_id, var_myhostname); + } + cleanup_out_format(state, REC_TYPE_NORM, "%sMessage-Id: <%s>", + state->resent, vstring_str(state->temp1)); + msg_info("%s: %smessage-id=<%s>", + state->queue_id, *state->resent ? "resent-" : "", + vstring_str(state->temp1)); + state->headers_seen |= (1 << (state->resent[0] ? + HDR_RESENT_MESSAGE_ID : HDR_MESSAGE_ID)); + } + if ((state->headers_seen & (1 << HDR_MESSAGE_ID)) == 0) + msg_info("%s: message-id=<>", state->queue_id); + + /* + * Add a missing (Resent-)Date: header. The date is in local time units, + * with the GMT offset at the end. + */ + if ((state->hdr_rewrite_context || var_always_add_hdrs) + && (state->headers_seen & (1 << (state->resent[0] ? + HDR_RESENT_DATE : HDR_DATE))) == 0) { + cleanup_out_format(state, REC_TYPE_NORM, "%sDate: %s", + state->resent, mail_date(state->arrival_time.tv_sec)); + } + + /* + * Add a missing (Resent-)From: header. + */ + if ((state->hdr_rewrite_context || var_always_add_hdrs) + && (state->headers_seen & (1 << (state->resent[0] ? + HDR_RESENT_FROM : HDR_FROM))) == 0) { + quote_822_local(state->temp1, *state->sender ? + state->sender : MAIL_ADDR_MAIL_DAEMON); + if (*state->sender && state->fullname && *state->fullname) { + char *cp; + + /* Enforce some sanity on full name content. */ + while ((cp = strchr(state->fullname, '\r')) != 0 + || (cp = strchr(state->fullname, '\n')) != 0) + *cp = ' '; + + switch (hfrom_format_code) { + + /* + * "From: phrase <route-addr>". Quote the phrase if it + * contains specials or the "%!" legacy address operators. + */ + case HFROM_FORMAT_CODE_STD: + vstring_sprintf(state->temp2, "%sFrom: ", state->resent); + if (state->fullname[strcspn(state->fullname, + "%!" LEX_822_SPECIALS)] == 0) { + /* Normalize whitespace. */ + token = tok822_scan_limit(state->fullname, &dummy_token, + var_token_limit); + } else { + token = tok822_alloc(TOK822_QSTRING, state->fullname); + } + if (token) { + tok822_externalize(state->temp2, token, TOK822_STR_NONE); + tok822_free(token); + vstring_strcat(state->temp2, " "); + } + vstring_sprintf_append(state->temp2, "<%s>", + vstring_str(state->temp1)); + break; + + /* + * "From: addr-spec (ctext)". This is the obsolete form. + */ + case HFROM_FORMAT_CODE_OBS: + vstring_sprintf(state->temp2, "%sFrom: %s ", + state->resent, vstring_str(state->temp1)); + vstring_sprintf(state->temp1, "(%s)", state->fullname); + token = tok822_parse(vstring_str(state->temp1)); + tok822_externalize(state->temp2, token, TOK822_STR_NONE); + tok822_free_tree(token); + break; + default: + msg_panic("%s: unknown header format %d", + myname, hfrom_format_code); + } + } + + /* + * "From: addr-spec". This is the form in the absence of full name + * information, also used for mail from mailer-daemon. + */ + else { + vstring_sprintf(state->temp2, "%sFrom: %s", + state->resent, vstring_str(state->temp1)); + } + CLEANUP_OUT_BUF(state, REC_TYPE_NORM, state->temp2); + } + + /* + * XXX 2821: Appendix B: The return address in the MAIL command SHOULD, + * if possible, be derived from the system's identity for the submitting + * (local) user, and the "From:" header field otherwise. If there is a + * system identity available, it SHOULD also be copied to the Sender + * header field if it is different from the address in the From header + * field. (Any Sender field that was already there SHOULD be removed.) + * Similar wording appears in RFC 2822 section 3.6.2. + * + * Postfix presently does not insert a Sender: header if envelope and From: + * address differ. Older Postfix versions assumed that the envelope + * sender address specifies the system identity and inserted Sender: + * whenever envelope and From: differed. This was wrong with relayed + * mail, and was often not even desirable with original submissions. + * + * XXX 2822 Section 3.6.2, as well as RFC 822 Section 4.1: FROM headers can + * contain multiple addresses. If this is the case, then a Sender: header + * must be provided with a single address. + * + * Postfix does not count the number of addresses in a From: header + * (although doing so is trivial, once the address is parsed). + */ + + /* + * Add a missing destination header. + */ +#define VISIBLE_RCPT ((1 << HDR_TO) | (1 << HDR_RESENT_TO) \ + | (1 << HDR_CC) | (1 << HDR_RESENT_CC)) + + if ((state->hdr_rewrite_context || var_always_add_hdrs) + && (state->headers_seen & VISIBLE_RCPT) == 0 && *var_rcpt_witheld) { + if (!is_header(var_rcpt_witheld)) { + msg_warn("bad %s header text \"%s\" -- " + "need \"headername: headervalue\"", + VAR_RCPT_WITHELD, var_rcpt_witheld); + } else { + cleanup_out_format(state, REC_TYPE_NORM, "%s", var_rcpt_witheld); + } + } + + /* + * Place a dummy PTR record right after the last header so that we can + * append headers without having to worry about clobbering the + * end-of-content marker. + */ + if (state->milters || cleanup_milters) { + if ((state->append_hdr_pt_offset = vstream_ftell(state->dst)) < 0) + msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path); + cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L); + if ((state->append_hdr_pt_target = vstream_ftell(state->dst)) < 0) + msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path); + state->body_offset = state->append_hdr_pt_target; + } +} + +/* cleanup_body_callback - output one body record */ + +static void cleanup_body_callback(void *context, int type, + const char *buf, ssize_t len, + off_t offset) +{ + CLEANUP_STATE *state = (CLEANUP_STATE *) context; + + /* + * XXX Workaround: when we reach the end of headers, mime_state_update() + * may execute up to three call-backs before returning to the caller: + * head_out(), head_end(), and body_out() or body_end(). As long as + * call-backs don't return a result, each call-back has to check for + * itself if the previous call-back experienced a problem. + */ + if (CLEANUP_OUT_OK(state) == 0) + return; + + /* + * Crude message body content filter for emergencies. This code has + * several problems: it sees one line at a time; it looks at long lines + * only in chunks of line_length_limit (2048) characters; it is easily + * bypassed with encodings and other tricks. + */ + if ((state->flags & CLEANUP_FLAG_FILTER) + && cleanup_body_checks + && (var_body_check_len == 0 || offset < var_body_check_len)) { + const char *value; + + if ((value = maps_find(cleanup_body_checks, buf, 0)) != 0) { + const char *result; + + if ((result = cleanup_act(state, CLEANUP_ACT_CTXT_BODY, + buf, value, VAR_BODY_CHECKS)) + == CLEANUP_ACT_DROP) { + return; + } else if (result != buf) { + cleanup_out(state, type, result, strlen(result)); + myfree((void *) result); + return; + } + } else if (cleanup_body_checks->error) { + msg_warn("%s: %s map lookup problem -- " + "message not accepted, try again later", + state->queue_id, cleanup_body_checks->title); + state->errs |= CLEANUP_STAT_WRITE; + } + } + cleanup_out(state, type, buf, len); +} + +/* cleanup_message_headerbody - process message content, header and body */ + +static void cleanup_message_headerbody(CLEANUP_STATE *state, int type, + const char *buf, ssize_t len) +{ + const char *myname = "cleanup_message_headerbody"; + const MIME_STATE_DETAIL *detail; + const char *cp; + char *dst; + + /* + * Replace each stray CR or LF with one space. These are not allowed in + * SMTP, and can be used to enable outbound (remote) SMTP smuggling. + * Replacing these early ensures that our later DKIM etc. signature will + * not be invalidated. Besides preventing SMTP smuggling, replacing stray + * <CR> or <LF> ensures that the result of signature validation by a + * later mail system will not depend on how that mail system handles + * those stray characters in an implementation-dependent manner. + * + * The input length is not changed, therefore it is safe to overwrite the + * input. + */ + if (var_cleanup_mask_stray_cr_lf) + for (dst = (char *) buf; dst < buf + len; dst++) + if (*dst == '\r' || *dst == '\n') + *dst = ' '; + + /* + * Reject unwanted characters. + * + * XXX Possible optimization: simplify the loop when the "reject" set + * contains only one character. + */ + if ((state->flags & CLEANUP_FLAG_FILTER) && cleanup_reject_chars) { + for (cp = buf; cp < buf + len; cp++) { + if (memchr(vstring_str(cleanup_reject_chars), + *(const unsigned char *) cp, + VSTRING_LEN(cleanup_reject_chars))) { + cleanup_act(state, CLEANUP_ACT_CTXT_ANY, + buf, "REJECT disallowed character", + "character reject"); + return; + } + } + } + + /* + * Strip unwanted characters. Don't overwrite the input. + * + * XXX Possible space+time optimization: use a bitset. + * + * XXX Possible optimization: simplify the loop when the "strip" set + * contains only one character. + * + * XXX Possible optimization: copy the input only if we really have to. + */ + if ((state->flags & CLEANUP_FLAG_FILTER) && cleanup_strip_chars) { + VSTRING_RESET(state->stripped_buf); + VSTRING_SPACE(state->stripped_buf, len + 1); + dst = vstring_str(state->stripped_buf); + for (cp = buf; cp < buf + len; cp++) + if (!memchr(vstring_str(cleanup_strip_chars), + *(const unsigned char *) cp, + VSTRING_LEN(cleanup_strip_chars))) + *dst++ = *cp; + *dst = 0; + buf = vstring_str(state->stripped_buf); + len = dst - buf; + } + + /* + * Copy text record to the output. + */ + if (type == REC_TYPE_NORM || type == REC_TYPE_CONT) { + state->mime_errs = mime_state_update(state->mime_state, type, buf, len); + } + + /* + * If we have reached the end of the message content segment, record the + * current file position so we can compute the message size lateron. + */ + else if (type == REC_TYPE_XTRA) { + state->mime_errs = mime_state_update(state->mime_state, type, buf, len); + if (state->milters || cleanup_milters) + /* Make room for body modification. */ + cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L); + /* Ignore header truncation after primary message headers. */ + state->mime_errs &= ~MIME_ERR_TRUNC_HEADER; + if (state->mime_errs && state->reason == 0) { + state->errs |= CLEANUP_STAT_CONT; + detail = mime_state_detail(state->mime_errs); + state->reason = dsn_prepend(detail->dsn, detail->text); + } + state->mime_state = mime_state_free(state->mime_state); + if ((state->xtra_offset = vstream_ftell(state->dst)) < 0) + msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path); + state->cont_length = state->xtra_offset - state->data_offset; + state->action = cleanup_extracted; + } + + /* + * This should never happen. + */ + else { + msg_warn("%s: message rejected: " + "unexpected record type %d in message content", myname, type); + state->errs |= CLEANUP_STAT_BAD; + } +} + +/* cleanup_mime_error_callback - error report call-back routine */ + +static void cleanup_mime_error_callback(void *context, int err_code, + const char *text, ssize_t len) +{ + CLEANUP_STATE *state = (CLEANUP_STATE *) context; + const char *origin; + + /* + * Message header too large errors are handled after the end of the + * primary message headers. + */ + if ((err_code & ~MIME_ERR_TRUNC_HEADER) != 0) { + if ((origin = nvtable_find(state->attr, MAIL_ATTR_LOG_ORIGIN)) == 0) + origin = MAIL_ATTR_ORG_NONE; +#define TEXT_LEN (len < 100 ? (int) len : 100) + msg_info("%s: reject: mime-error %s: %.*s from %s; from=<%s> to=<%s>", + state->queue_id, mime_state_error(err_code), TEXT_LEN, text, + origin, info_log_addr_form_sender(state->sender), + info_log_addr_form_recipient(state->recip ? + state->recip : "unknown")); + } +} + +/* cleanup_message - initialize message content segment */ + +void cleanup_message(CLEANUP_STATE *state, int type, const char *buf, ssize_t len) +{ + const char *myname = "cleanup_message"; + int mime_options; + + /* + * Write the start-of-content segment marker. + */ + cleanup_out_string(state, REC_TYPE_MESG, ""); + if ((state->data_offset = vstream_ftell(state->dst)) < 0) + msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path); + + /* + * Set up MIME processing options, if any. MIME_OPT_DISABLE_MIME disables + * special processing of Content-Type: headers, and thus, causes all text + * after the primary headers to be treated as the message body. + */ + mime_options = 0; + if (var_disable_mime_input) { + mime_options |= MIME_OPT_DISABLE_MIME; + } else { + /* Turn off content checks if bouncing or forwarding mail. */ + if (state->flags & CLEANUP_FLAG_FILTER) { + if (var_strict_8bitmime || var_strict_7bit_hdrs) + mime_options |= MIME_OPT_REPORT_8BIT_IN_HEADER; + if (var_strict_8bitmime || var_strict_8bit_body) + mime_options |= MIME_OPT_REPORT_8BIT_IN_7BIT_BODY; + if (var_strict_encoding) + mime_options |= MIME_OPT_REPORT_ENCODING_DOMAIN; + if (var_strict_8bitmime || var_strict_7bit_hdrs + || var_strict_8bit_body || var_strict_encoding + || *var_header_checks || *var_mimehdr_checks + || *var_nesthdr_checks) + mime_options |= MIME_OPT_REPORT_NESTING; + } + } + state->mime_state = mime_state_alloc(mime_options, + cleanup_header_callback, + cleanup_header_done_callback, + cleanup_body_callback, + (MIME_STATE_ANY_END) 0, + cleanup_mime_error_callback, + (void *) state); + + /* + * XXX Workaround: truncate a long message header so that we don't exceed + * the default Sendmail libmilter request size limit of 65535. + */ +#define KLUDGE_HEADER_LIMIT 60000 + if ((cleanup_milters || state->milters) + && var_header_limit > KLUDGE_HEADER_LIMIT) + var_header_limit = KLUDGE_HEADER_LIMIT; + + /* + * Pass control to the header processing routine. + */ + state->action = cleanup_message_headerbody; + cleanup_message_headerbody(state, type, buf, len); +} diff --git a/src/cleanup/cleanup_milter.c b/src/cleanup/cleanup_milter.c new file mode 100644 index 0000000..b6a1ec4 --- /dev/null +++ b/src/cleanup/cleanup_milter.c @@ -0,0 +1,2748 @@ +/*++ +/* NAME +/* cleanup_milter 3 +/* SUMMARY +/* external mail filter support +/* SYNOPSIS +/* #include <cleanup.h> +/* +/* void cleanup_milter_header_checks_init(void) +/* +/* void cleanup_milter_receive(state, count) +/* CLEANUP_STATE *state; +/* int count; +/* +/* void cleanup_milter_inspect(state, milters) +/* CLEANUP_STATE *state; +/* MILTERS *milters; +/* +/* cleanup_milter_emul_mail(state, milters, sender) +/* CLEANUP_STATE *state; +/* MILTERS *milters; +/* const char *sender; +/* +/* cleanup_milter_emul_rcpt(state, milters, recipient) +/* CLEANUP_STATE *state; +/* MILTERS *milters; +/* const char *recipient; +/* +/* cleanup_milter_emul_data(state, milters) +/* CLEANUP_STATE *state; +/* MILTERS *milters; +/* DESCRIPTION +/* This module implements support for Sendmail-style mail +/* filter (milter) applications, including in-place queue file +/* modification. +/* +/* cleanup_milter_header_checks_init() does pre-jail +/* initializations. +/* +/* cleanup_milter_receive() receives mail filter definitions, +/* typically from an smtpd(8) server process, and registers +/* local call-back functions for macro expansion and for queue +/* file modification. +/* +/* cleanup_milter_inspect() sends the current message headers +/* and body to the mail filters that were received with +/* cleanup_milter_receive(), or that are specified with the +/* cleanup_milters configuration parameter. +/* +/* cleanup_milter_emul_mail() emulates connect, helo and mail +/* events for mail that does not arrive via the smtpd(8) server. +/* The emulation pretends that mail arrives from localhost/127.0.0.1 +/* via ESMTP. Milters can reject emulated connect, helo, mail +/* or data events, but not emulated rcpt events as described +/* next. +/* +/* cleanup_milter_emul_rcpt() emulates an rcpt event for mail +/* that does not arrive via the smtpd(8) server. This reports +/* a server configuration error condition when the milter +/* rejects an emulated rcpt event. +/* +/* cleanup_milter_emul_data() emulates a data event for mail +/* that does not arrive via the smtpd(8) server. It's OK for +/* milters to reject emulated data events. +/* SEE ALSO +/* milter(3) generic mail filter interface +/* DIAGNOSTICS +/* Fatal errors: memory allocation problem. +/* Panic: interface violation. +/* Warnings: I/O errors (state->errs is updated accordingly). +/* 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/socket.h> /* AF_INET */ +#include <string.h> +#include <errno.h> + +#ifdef STRCASECMP_IN_STRINGS_H +#include <strings.h> +#endif + +/* Utility library. */ + +#include <msg.h> +#include <vstream.h> +#include <vstring.h> +#include <stringops.h> +#include <inet_proto.h> + +/* Global library. */ + +#include <off_cvt.h> +#include <dsn_mask.h> +#include <rec_type.h> +#include <cleanup_user.h> +#include <record.h> +#include <rec_attr_map.h> +#include <mail_proto.h> +#include <mail_params.h> +#include <lex_822.h> +#include <is_header.h> +#include <quote_821_local.h> +#include <dsn_util.h> +#include <xtext.h> +#include <info_log_addr_form.h> + +/* Application-specific. */ + +#include <cleanup.h> + + /* + * How Postfix 2.4 edits queue file information: + * + * Mail filter applications (Milters) can send modification requests after + * receiving the end of the message body. Postfix implements these + * modifications in the cleanup server, so that it can edit the queue file + * in place. This avoids the temporary files that would be needed when + * modifications were implemented in the SMTP server (Postfix normally does + * not store the whole message in main memory). Once a Milter is done + * editing, the queue file can be used as input for the next Milter, and so + * on. Finally, the cleanup server changes file permissions, calls fsync(), + * and waits for successful completion. + * + * To implement in-place queue file edits, we need to introduce surprisingly + * little change to the existing Postfix queue file structure. All we need + * is a way to mark a record as deleted, and to jump from one place in the + * queue file to another. We could implement deleted records with jumps, but + * marking is sometimes simpler. + * + * Postfix does not store queue files as plain text files. Instead all + * information is stored in records with an explicit type and length, for + * sender, recipient, arrival time, and so on. Even the content that makes + * up the message header and body is stored as records with explicit types + * and lengths. This organization makes it very easy to mark a record as + * deleted, and to introduce the pointer records that we will use to jump + * from one place in a queue file to another place. + * + * - Deleting a recipient is easiest - simply modify the record type into one + * that is skipped by the software that delivers mail. We won't try to reuse + * the deleted recipient for other purposes. When deleting a recipient, we + * may need to delete multiple recipient records that result from virtual + * alias expansion of the original recipient address. + * + * - Replacing a header record involves pointer records. A record is replaced + * by overwriting it with a forward pointer to space after the end of the + * queue file, putting the new record there, followed by a reverse pointer + * to the record that follows the replaced header. To simplify + * implementation we follow a short header record with a filler record so + * that we can always overwrite a header record with a pointer. + * + * N.B. This is a major difference with Postfix version 2.3, which needed + * complex code to save records that follow a short header, before it could + * overwrite a short header record. This code contained two of the three + * post-release bugs that were found with Postfix header editing. + * + * - Inserting a header record is like replacing one, except that we also + * relocate the record that is being overwritten by the forward pointer. + * + * - Deleting a message header is simplest when we replace it by a "skip" + * pointer to the information that follows the header. With a multi-line + * header we need to update only the first line. + * + * - Appending a recipient or header record involves pointer records as well. + * To make this convenient, the queue file already contains dummy pointer + * records at the locations where we want to append recipient or header + * content. To append, change the dummy pointer into a forward pointer to + * space after the end of a message, put the new recipient or header record + * there, followed by a reverse pointer to the record that follows the + * forward pointer. + * + * - To append another header or recipient record, replace the reverse pointer + * by a forward pointer to space after the end of a message, put the new + * record there, followed by the value of the reverse pointer that we + * replace. Thus, there is no one-to-one correspondence between forward and + * backward pointers. Instead, there can be multiple forward pointers for + * one reverse pointer. + * + * - When a mail filter wants to replace an entire body, we overwrite existing + * body records until we run out of space, and then write a pointer to space + * after the end of the queue file, followed by more body content. There may + * be multiple regions with body content; regions are connected by forward + * pointers, and the last region ends with a pointer to the marker that ends + * the message content segment. Body regions can be large and therefore they + * are reused to avoid wasting space. Sendmail mail filters currently do not + * replace individual body records, and that is a good thing. + * + * Making queue file modifications safe: + * + * Postfix queue files are segmented. The first segment is for envelope + * records, the second for message header and body content, and the third + * segment is for information that was extracted or generated from the + * message header or body content. Each segment is terminated by a marker + * record. For now we don't want to change their location. That is, we want + * to avoid moving the records that mark the start or end of a queue file + * segment. + * + * To ensure that we can always replace a header or body record by a pointer + * record, without having to relocate a marker record, the cleanup server + * places a dummy pointer record at the end of the recipients and at the end + * of the message header. To support message body modifications, a dummy + * pointer record is also placed at the end of the message content. + * + * With all these changes in queue file organization, REC_TYPE_END is no longer + * guaranteed to be the last record in a queue file. If an application were + * to read beyond the REC_TYPE_END marker, it would go into an infinite + * loop, because records after REC_TYPE_END alternate with reverse pointers + * to the middle of the queue file. For robustness, the record reading + * routine skips forward to the end-of-file position after reading the + * REC_TYPE_END marker. + */ + +/*#define msg_verbose 2*/ + +static HBC_CHECKS *cleanup_milter_hbc_checks; +static VSTRING *cleanup_milter_hbc_reply; +static void cleanup_milter_set_error(CLEANUP_STATE *, int); +static const char *cleanup_add_rcpt_par(void *, const char *, const char *); + +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) + +/* cleanup_milter_hbc_log - log post-milter header/body_checks action */ + +static void cleanup_milter_hbc_log(void *context, const char *action, + const char *where, const char *line, + const char *optional_text) +{ + const CLEANUP_STATE *state = (CLEANUP_STATE *) context; + const char *attr; + + vstring_sprintf(state->temp1, "%s: milter-%s-%s: %s %.60s from %s[%s];", + state->queue_id, where, action, where, line, + state->client_name, state->client_addr); + if (state->sender) + vstring_sprintf_append(state->temp1, " from=<%s>", + info_log_addr_form_sender(state->sender)); + if (state->recip) + vstring_sprintf_append(state->temp1, " to=<%s>", + info_log_addr_form_recipient(state->recip)); + if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_PROTO_NAME)) != 0) + vstring_sprintf_append(state->temp1, " proto=%s", attr); + if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_HELO_NAME)) != 0) + vstring_sprintf_append(state->temp1, " helo=<%s>", attr); + if (optional_text) + vstring_sprintf_append(state->temp1, ": %s", optional_text); + msg_info("%s", vstring_str(state->temp1)); +} + +/* cleanup_milter_header_prepend - prepend header to milter-generated header */ + +static void cleanup_milter_header_prepend(void *context, int rec_type, + const char *buf, ssize_t len, off_t offset) +{ + /* XXX save prepended header to buffer. */ + msg_warn("the milter_header/body_checks prepend action is not implemented"); +} + +/* cleanup_milter_hbc_extend - additional header/body_checks actions */ + +static char *cleanup_milter_hbc_extend(void *context, const char *command, + ssize_t cmd_len, const char *optional_text, + const char *where, const char *buf, + ssize_t buf_len, off_t offset) +{ + CLEANUP_STATE *state = (CLEANUP_STATE *) context; + const char *map_class = VAR_MILT_HEAD_CHECKS; /* XXX */ + +#define STREQUAL(x,y,l) (strncasecmp((x), (y), (l)) == 0 && (y)[l] == 0) + + /* + * These are currently our mutually-exclusive ways of not receiving mail: + * "reject" and "discard". Only these can be reported to the up-stream + * Postfix libmilter code, because sending any reply there causes Postfix + * libmilter to skip further "edit" requests. By way of safety net, each + * of these must also reset CLEANUP_FLAG_FILTER_ALL. + */ +#define CLEANUP_MILTER_REJECTING_OR_DISCARDING_MESSAGE(state) \ + ((state->flags & CLEANUP_FLAG_DISCARD) || (state->errs & CLEANUP_STAT_CONT)) + + /* + * We log all header/body-checks actions here, because we know the + * details of the message content that triggered the action. We report + * detail-free milter-reply values (reject/discard, stored in the + * milter_hbc_reply state member) to the Postfix libmilter code, so that + * Postfix libmilter can stop sending requests. + * + * We also set all applicable cleanup flags here, because there is no + * guarantee that Postfix libmilter will propagate our own milter-reply + * value to cleanup_milter_inspect() which calls cleanup_milter_apply(). + * The latter translates responses from Milter applications into cleanup + * flags, and logs the response text. Postfix libmilter can convey only + * one milter-reply value per email message, and that reply may even come + * from outside Postfix. + * + * To suppress redundant logging, cleanup_milter_apply() does nothing when + * the milter-reply value matches the saved text in the milter_hbc_reply + * state member. As we remember only one milter-reply value, we can't + * report multiple milter-reply values per email message. We satisfy this + * constraint, because we already clear the CLEANUP_FLAG_FILTER_ALL flags + * to terminate further header inspection. + */ + if ((state->flags & CLEANUP_FLAG_FILTER_ALL) == 0) + return ((char *) buf); + + if (STREQUAL(command, "BCC", cmd_len)) { + if (strchr(optional_text, '@') == 0) { + msg_warn("bad BCC address \"%s\" in %s map -- " + "need user@domain", + optional_text, VAR_MILT_HEAD_CHECKS); + } else { + cleanup_milter_hbc_log(context, "bcc", where, buf, optional_text); + /* Caller checks state error flags. */ + (void) cleanup_add_rcpt_par(state, optional_text, ""); + } + return ((char *) buf); + } + if (STREQUAL(command, "REJECT", cmd_len)) { + const CLEANUP_STAT_DETAIL *detail; + + if (state->reason) + myfree(state->reason); + detail = cleanup_stat_detail(CLEANUP_STAT_CONT); + if (*optional_text) { + state->reason = dsn_prepend(detail->dsn, optional_text); + if (*state->reason != '4' && *state->reason != '5') { + msg_warn("bad DSN action in %s -- need 4.x.x or 5.x.x", + optional_text); + *state->reason = '4'; + } + } else { + state->reason = dsn_prepend(detail->dsn, detail->text); + } + if (*state->reason == '4') + state->errs |= CLEANUP_STAT_DEFER; + else + state->errs |= CLEANUP_STAT_CONT; + state->flags &= ~CLEANUP_FLAG_FILTER_ALL; + cleanup_milter_hbc_log(context, "reject", where, buf, state->reason); + vstring_sprintf(cleanup_milter_hbc_reply, "%d %s", + detail->smtp, state->reason); + STR(cleanup_milter_hbc_reply)[0] = *state->reason; + return ((char *) buf); + } + if (STREQUAL(command, "FILTER", cmd_len)) { + if (*optional_text == 0) { + msg_warn("missing FILTER command argument in %s map", map_class); + } else if (strchr(optional_text, ':') == 0) { + msg_warn("bad FILTER command %s in %s -- " + "need transport:destination", + optional_text, map_class); + } else { + if (state->filter) + myfree(state->filter); + state->filter = mystrdup(optional_text); + cleanup_milter_hbc_log(context, "filter", where, buf, + optional_text); + } + return ((char *) buf); + } + if (STREQUAL(command, "DISCARD", cmd_len)) { + cleanup_milter_hbc_log(context, "discard", where, buf, optional_text); + vstring_strcpy(cleanup_milter_hbc_reply, "D"); + state->flags |= CLEANUP_FLAG_DISCARD; + state->flags &= ~CLEANUP_FLAG_FILTER_ALL; + return ((char *) buf); + } + if (STREQUAL(command, "HOLD", cmd_len)) { + if ((state->flags & (CLEANUP_FLAG_HOLD | CLEANUP_FLAG_DISCARD)) == 0) { + cleanup_milter_hbc_log(context, "hold", where, buf, optional_text); + state->flags |= CLEANUP_FLAG_HOLD; + } + return ((char *) buf); + } + if (STREQUAL(command, "REDIRECT", cmd_len)) { + if (strchr(optional_text, '@') == 0) { + msg_warn("bad REDIRECT target \"%s\" in %s map -- " + "need user@domain", + optional_text, map_class); + } else { + if (state->redirect) + myfree(state->redirect); + state->redirect = mystrdup(optional_text); + cleanup_milter_hbc_log(context, "redirect", where, buf, + optional_text); + state->flags &= ~CLEANUP_FLAG_FILTER_ALL; + } + return ((char *) buf); + } + return ((char *) HBC_CHECKS_STAT_UNKNOWN); +} + +/* cleanup_milter_header_checks - inspect Milter-generated header */ + +static int cleanup_milter_header_checks(CLEANUP_STATE *state, VSTRING *buf) +{ + char *ret; + + /* + * Milter application "add/insert/replace header" requests happen at the + * end-of-message stage, therefore all the header operations are relative + * to the primary message header. + */ + ret = hbc_header_checks((void *) state, cleanup_milter_hbc_checks, + MIME_HDR_PRIMARY, (HEADER_OPTS *) 0, + buf, (off_t) 0); + if (ret == 0) { + return (0); + } else if (ret == HBC_CHECKS_STAT_ERROR) { + msg_warn("%s: %s map lookup problem -- " + "message not accepted, try again later", + state->queue_id, VAR_MILT_HEAD_CHECKS); + state->errs |= CLEANUP_STAT_WRITE; + return (0); + } else { + if (ret != STR(buf)) { + vstring_strcpy(buf, ret); + myfree(ret); + } + return (1); + } +} + +/* cleanup_milter_hbc_add_meta_records - add REDIRECT or FILTER meta records */ + +static void cleanup_milter_hbc_add_meta_records(CLEANUP_STATE *state) +{ + const char *myname = "cleanup_milter_hbc_add_meta_records"; + off_t reverse_ptr_offset; + off_t new_meta_offset; + + /* + * Note: this code runs while the Milter infrastructure is being torn + * down. For this reason we handle all I/O errors here on the spot, + * instead of reporting them back through the Milter infrastructure. + */ + + /* + * Sanity check. + */ + if (state->append_meta_pt_offset < 0) + msg_panic("%s: no meta append pointer location", myname); + if (state->append_meta_pt_target < 0) + msg_panic("%s: no meta append pointer target", myname); + + /* + * Allocate space after the end of the queue file, and write the meta + * record(s), followed by a reverse pointer record that points to the + * target of the old "meta record append" pointer record. This reverse + * pointer record becomes the new "meta record append" pointer record. + * Although the new "meta record append" pointer record will never be + * used, we update it here to make the code more similar to other code + * that inserts/appends content, so that common code can be factored out + * later. + */ + if ((new_meta_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) { + cleanup_milter_set_error(state, errno); + return; + } + if (state->filter != 0) + cleanup_out_string(state, REC_TYPE_FILT, state->filter); + if (state->redirect != 0) + cleanup_out_string(state, REC_TYPE_RDR, state->redirect); + if ((reverse_ptr_offset = vstream_ftell(state->dst)) < 0) { + msg_warn("%s: vstream_ftell file %s: %m", myname, cleanup_path); + state->errs |= CLEANUP_STAT_WRITE; + return; + } + cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, + (long) state->append_meta_pt_target); + + /* + * Pointer flipping: update the old "meta record append" pointer record + * value with the location of the new meta record. + */ + if (vstream_fseek(state->dst, state->append_meta_pt_offset, SEEK_SET) < 0) { + cleanup_milter_set_error(state, errno); + return; + } + cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, + (long) new_meta_offset); + + /* + * Update the in-memory "meta append" pointer record location with the + * location of the reverse pointer record that follows the new meta + * record. The target of the "meta append" pointer record does not + * change; it's always the record that follows the dummy pointer record + * that was written while Postfix received the message. + */ + state->append_meta_pt_offset = reverse_ptr_offset; + + /* + * Note: state->append_meta_pt_target never changes. + */ +} + +/* cleanup_milter_header_checks_init - initialize post-Milter header checks */ + +void cleanup_milter_header_checks_init(void) +{ + static const char myname[] = "cleanup_milter_header_checks_init"; + +#define NO_NESTED_HDR_NAME "" +#define NO_NESTED_HDR_VALUE "" +#define NO_MIME_HDR_NAME "" +#define NO_MIME_HDR_VALUE "" + + static /* XXX not const */ HBC_CALL_BACKS call_backs = { + cleanup_milter_hbc_log, + cleanup_milter_header_prepend, + cleanup_milter_hbc_extend, + }; + + if (*var_milt_head_checks == 0) + msg_panic("%s: %s is empty", myname, VAR_MILT_HEAD_CHECKS); + + if (cleanup_milter_hbc_checks) + msg_panic("%s: cleanup_milter_hbc_checks is not null", myname); + cleanup_milter_hbc_checks = + hbc_header_checks_create(VAR_MILT_HEAD_CHECKS, var_milt_head_checks, + NO_MIME_HDR_NAME, NO_MIME_HDR_VALUE, + NO_NESTED_HDR_NAME, NO_NESTED_HDR_VALUE, + &call_backs); + + if (cleanup_milter_hbc_reply) + msg_panic("%s: cleanup_milter_hbc_reply is not null", myname); + cleanup_milter_hbc_reply = vstring_alloc(100); +} + +#ifdef TEST + +/* cleanup_milter_header_checks_deinit - undo cleanup_milter_header_checks_init */ + +static void cleanup_milter_header_checks_deinit(void) +{ + static const char myname[] = "cleanup_milter_header_checks_deinit"; + + if (cleanup_milter_hbc_checks == 0) + msg_panic("%s: cleanup_milter_hbc_checks is null", myname); + hbc_header_checks_free(cleanup_milter_hbc_checks); + cleanup_milter_hbc_checks = 0; + + if (cleanup_milter_hbc_reply == 0) + msg_panic("%s: cleanup_milter_hbc_reply is null", myname); + vstring_free(cleanup_milter_hbc_reply); + cleanup_milter_hbc_reply = 0; +} + +#endif + +/* cleanup_milter_header_checks_reinit - re-init post-Milter header checks */ + +static void cleanup_milter_header_checks_reinit(CLEANUP_STATE *state) +{ + if (state->filter) + myfree(state->filter); + state->filter = 0; + if (state->redirect) + myfree(state->redirect); + state->redirect = 0; + VSTRING_RESET(cleanup_milter_hbc_reply); +} + +/* cleanup_milter_hbc_finish - finalize post-Milter header checks */ + +static void cleanup_milter_hbc_finish(CLEANUP_STATE *state) +{ + if (CLEANUP_OUT_OK(state) + && !CLEANUP_MILTER_REJECTING_OR_DISCARDING_MESSAGE(state) + && (state->filter || state->redirect)) + cleanup_milter_hbc_add_meta_records(state); +} + + /* + * Milter replies. + */ +#define CLEANUP_MILTER_SET_REASON(__state, __reason) do { \ + if ((__state)->reason) \ + myfree((__state)->reason); \ + (__state)->reason = mystrdup(__reason); \ + if ((__state)->smtp_reply) { \ + myfree((__state)->smtp_reply); \ + (__state)->smtp_reply = 0; \ + } \ + } while (0) + +#define CLEANUP_MILTER_SET_SMTP_REPLY(__state, __smtp_reply) do { \ + if ((__state)->reason) \ + myfree((__state)->reason); \ + (__state)->reason = mystrdup(__smtp_reply + 4); \ + printable((__state)->reason, '_'); \ + if ((__state)->smtp_reply) \ + myfree((__state)->smtp_reply); \ + (__state)->smtp_reply = mystrdup(__smtp_reply); \ + } while (0) + +/* cleanup_milter_set_error - set error flag from errno */ + +static void cleanup_milter_set_error(CLEANUP_STATE *state, int err) +{ + if (err == EFBIG) { + msg_warn("%s: queue file size limit exceeded", state->queue_id); + state->errs |= CLEANUP_STAT_SIZE; + } else { + msg_warn("%s: write queue file: %m", state->queue_id); + state->errs |= CLEANUP_STAT_WRITE; + } +} + +/* cleanup_milter_error - return dummy error description */ + +static const char *cleanup_milter_error(CLEANUP_STATE *state, int err) +{ + const char *myname = "cleanup_milter_error"; + const CLEANUP_STAT_DETAIL *dp; + + /* + * For consistency with error reporting within the milter infrastructure, + * content manipulation routines return a null pointer on success, and an + * SMTP-like response on error. + * + * However, when cleanup_milter_apply() receives this error response from + * the milter infrastructure, it ignores the text since the appropriate + * cleanup error flags were already set by cleanup_milter_set_error(). + * + * Specify a null error number when the "errno to error flag" mapping was + * already done elsewhere, possibly outside this module. + */ + if (err) + cleanup_milter_set_error(state, err); + else if (CLEANUP_OUT_OK(state)) + msg_panic("%s: missing errno to error flag mapping", myname); + if (state->milter_err_text == 0) + state->milter_err_text = vstring_alloc(50); + dp = cleanup_stat_detail(state->errs); + return (STR(vstring_sprintf(state->milter_err_text, + "%d %s %s", dp->smtp, dp->dsn, dp->text))); +} + +/* cleanup_add_header - append message header */ + +static const char *cleanup_add_header(void *context, const char *name, + const char *space, + const char *value) +{ + const char *myname = "cleanup_add_header"; + CLEANUP_STATE *state = (CLEANUP_STATE *) context; + VSTRING *buf; + off_t reverse_ptr_offset; + off_t new_hdr_offset; + + /* + * To simplify implementation, the cleanup server writes a dummy "header + * append" pointer record after the last message header. We cache both + * the location and the target of the current "header append" pointer + * record. + */ + if (state->append_hdr_pt_offset < 0) + msg_panic("%s: no header append pointer location", myname); + if (state->append_hdr_pt_target < 0) + msg_panic("%s: no header append pointer target", myname); + + /* + * Return early when Milter header checks request that this header record + * be dropped, or that the message is discarded. Note: CLEANUP_OUT_OK() + * tests CLEANUP_FLAG_DISCARD. We don't want to report the latter as an + * error. + */ + buf = vstring_alloc(100); + vstring_sprintf(buf, "%s:%s%s", name, space, value); + if (cleanup_milter_hbc_checks) { + if (cleanup_milter_header_checks(state, buf) == 0 + || (state->flags & CLEANUP_FLAG_DISCARD)) { + vstring_free(buf); + return (0); + } + if (CLEANUP_OUT_OK(state) == 0) { + vstring_free(buf); + return (cleanup_milter_error(state, 0)); + } + } + + /* + * Allocate space after the end of the queue file, and write the header + * record(s), followed by a reverse pointer record that points to the + * target of the old "header append" pointer record. This reverse pointer + * record becomes the new "header append" pointer record. + */ + if ((new_hdr_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + vstring_free(buf); + return (cleanup_milter_error(state, errno)); + } + /* XXX emit prepended header, then clear it. */ + cleanup_out_header(state, buf); /* Includes padding */ + vstring_free(buf); + if ((reverse_ptr_offset = vstream_ftell(state->dst)) < 0) { + msg_warn("%s: vstream_ftell file %s: %m", myname, cleanup_path); + return (cleanup_milter_error(state, errno)); + } + cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, + (long) state->append_hdr_pt_target); + + /* + * Pointer flipping: update the old "header append" pointer record value + * with the location of the new header record. + * + * XXX To avoid unnecessary seek operations when the new header immediately + * follows the old append header pointer, write a null pointer or make + * the record reading loop smarter. Making vstream_fseek() smarter does + * not help, because it doesn't know if we're going to read or write + * after a write+seek sequence. + */ + if (vstream_fseek(state->dst, state->append_hdr_pt_offset, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + return (cleanup_milter_error(state, errno)); + } + cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, + (long) new_hdr_offset); + + /* + * Update the in-memory "header append" pointer record location with the + * location of the reverse pointer record that follows the new header. + * The target of the "header append" pointer record does not change; it's + * always the record that follows the dummy pointer record that was + * written while Postfix received the message. + */ + state->append_hdr_pt_offset = reverse_ptr_offset; + + /* + * In case of error while doing record output. + */ + return (CLEANUP_OUT_OK(state) == 0 ? cleanup_milter_error(state, 0) : + cleanup_milter_hbc_reply && LEN(cleanup_milter_hbc_reply) ? + STR(cleanup_milter_hbc_reply) : 0); + + /* + * Note: state->append_hdr_pt_target never changes. + */ +} + +/* cleanup_find_header_start - find specific header instance */ + +static off_t cleanup_find_header_start(CLEANUP_STATE *state, ssize_t index, + const char *header_label, + VSTRING *buf, + int *prec_type, + int allow_ptr_backup, + int skip_headers) +{ + const char *myname = "cleanup_find_header_start"; + off_t curr_offset; /* offset after found record */ + off_t ptr_offset; /* pointer to found record */ + VSTRING *ptr_buf = 0; + int rec_type = REC_TYPE_ERROR; + int last_type; + ssize_t len; + int hdr_count = 0; + + if (msg_verbose) + msg_info("%s: index %ld name \"%s\"", + myname, (long) index, header_label ? header_label : "(none)"); + + /* + * Sanity checks. + */ + if (index < 1) + msg_panic("%s: bad header index %ld", myname, (long) index); + + /* + * Skip to the start of the message content, and read records until we + * either find the specified header, or until we hit the end of the + * headers. + * + * The index specifies the header instance: 1 is the first one. The header + * label specifies the header name. A null pointer matches any header. + * + * When the specified header is not found, the result value is -1. + * + * When the specified header is found, its first record is stored in the + * caller-provided read buffer, and the result value is the queue file + * offset of that record. The file read position is left at the start of + * the next (non-filler) queue file record, which can be the remainder of + * a multi-record header. + * + * When a header is found and allow_ptr_backup is non-zero, then the result + * is either the first record of that header, or it is the pointer record + * that points to the first record of that header. In the latter case, + * the file read position is undefined. Returning the pointer allows us + * to do some optimizations when inserting text multiple times at the + * same place. + * + * XXX We can't use the MIME processor here. It not only buffers up the + * input, it also reads the record that follows a complete header before + * it invokes the header call-back action. This complicates the way that + * we discover header offsets and boundaries. Worse is that the MIME + * processor is unaware that multi-record message headers can have PTR + * records in the middle. + * + * XXX The draw-back of not using the MIME processor is that we have to + * duplicate some of its logic here and in the routine that finds the end + * of the header record. To minimize the duplication we define an ugly + * macro that is used in all code that scans for header boundaries. + * + * XXX Sendmail compatibility (based on Sendmail 8.13.6 measurements). + * + * - When changing Received: header #1, we change the Received: header that + * follows our own one; a request to change Received: header #0 is + * silently treated as a request to change Received: header #1. + * + * - When changing Date: header #1, we change the first Date: header; a + * request to change Date: header #0 is silently treated as a request to + * change Date: header #1. + * + * Thus, header change requests are relative to the content as received, + * that is, the content after our own Received: header. They can affect + * only the headers that the MTA actually exposes to mail filter + * applications. + * + * - However, when inserting a header at position 0, the new header appears + * before our own Received: header, and when inserting at position 1, the + * new header appears after our own Received: header. + * + * Thus, header insert operations are relative to the content as delivered, + * that is, the content including our own Received: header. + * + * None of the above is applicable after a Milter inserts a header before + * our own Received: header. From then on, our own Received: header + * becomes just like other headers. + */ +#define CLEANUP_FIND_HEADER_NOTFOUND (-1) +#define CLEANUP_FIND_HEADER_IOERROR (-2) + +#define CLEANUP_FIND_HEADER_RETURN(offs) do { \ + if (ptr_buf) \ + vstring_free(ptr_buf); \ + return (offs); \ + } while (0) + +#define GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, buf, curr_offset, quit) \ + if ((rec_type = rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE)) < 0) { \ + msg_warn("%s: read file %s: %m", myname, cleanup_path); \ + cleanup_milter_set_error(state, errno); \ + do { quit; } while (0); \ + } \ + if (msg_verbose > 1) \ + msg_info("%s: read: %ld: %.*s", myname, (long) curr_offset, \ + LEN(buf) > 30 ? 30 : (int) LEN(buf), STR(buf)); \ + if (rec_type == REC_TYPE_DTXT) \ + continue; \ + if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT \ + && rec_type != REC_TYPE_PTR) \ + break; + /* End of hairy macros. */ + + if (vstream_fseek(state->dst, state->data_offset, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + cleanup_milter_set_error(state, errno); + CLEANUP_FIND_HEADER_RETURN(CLEANUP_FIND_HEADER_IOERROR); + } + for (ptr_offset = 0, last_type = 0; /* void */ ; /* void */ ) { + if ((curr_offset = vstream_ftell(state->dst)) < 0) { + msg_warn("%s: vstream_ftell file %s: %m", myname, cleanup_path); + cleanup_milter_set_error(state, errno); + CLEANUP_FIND_HEADER_RETURN(CLEANUP_FIND_HEADER_IOERROR); + } + /* Don't follow the "append header" pointer. */ + if (curr_offset == state->append_hdr_pt_offset) + break; + /* Caution: this macro terminates the loop at end-of-message. */ + /* Don't do complex processing while breaking out of this loop. */ + GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, buf, curr_offset, + CLEANUP_FIND_HEADER_RETURN(CLEANUP_FIND_HEADER_IOERROR)); + /* Caution: don't assume ptr->header. This may be header-ptr->body. */ + if (rec_type == REC_TYPE_PTR) { + if (rec_goto(state->dst, STR(buf)) < 0) { + msg_warn("%s: read file %s: %m", myname, cleanup_path); + cleanup_milter_set_error(state, errno); + CLEANUP_FIND_HEADER_RETURN(CLEANUP_FIND_HEADER_IOERROR); + } + /* Save PTR record, in case it points to the start of a header. */ + if (allow_ptr_backup) { + ptr_offset = curr_offset; + if (ptr_buf == 0) + ptr_buf = vstring_alloc(100); + vstring_strcpy(ptr_buf, STR(buf)); + } + /* Don't update last_type; PTR can happen after REC_TYPE_CONT. */ + continue; + } + /* The middle of a multi-record header. */ + else if (last_type == REC_TYPE_CONT || IS_SPACE_TAB(STR(buf)[0])) { + /* Reset the saved PTR record and update last_type. */ + } + /* No more message headers. */ + else if ((len = is_header(STR(buf))) == 0) { + break; + } + /* This the start of a message header. */ + else if (hdr_count++ < skip_headers) + /* Reset the saved PTR record and update last_type. */ ; + else if ((header_label == 0 + || (strncasecmp(header_label, STR(buf), len) == 0 + && (strlen(header_label) == len))) + && --index == 0) { + /* If we have a saved PTR record, it points to start of header. */ + break; + } + ptr_offset = 0; + last_type = rec_type; + } + + /* + * In case of failure, return negative start position. + */ + if (index > 0) { + curr_offset = CLEANUP_FIND_HEADER_NOTFOUND; + } else { + + /* + * Skip over short-header padding, so that the file read pointer is + * always positioned at the first non-padding record after the header + * record. Insist on padding after short a header record, so that a + * short header record can safely be overwritten by a pointer record. + */ + if (LEN(buf) < REC_TYPE_PTR_PAYL_SIZE) { + VSTRING *rbuf = (ptr_offset ? buf : + (ptr_buf ? ptr_buf : + (ptr_buf = vstring_alloc(100)))); + int rval; + + if ((rval = rec_get_raw(state->dst, rbuf, 0, REC_FLAG_NONE)) < 0) { + cleanup_milter_set_error(state, errno); + CLEANUP_FIND_HEADER_RETURN(CLEANUP_FIND_HEADER_IOERROR); + } + if (rval != REC_TYPE_DTXT) + msg_panic("%s: short header without padding", myname); + } + + /* + * Optionally return a pointer to the message header, instead of the + * start of the message header itself. In that case the file read + * position is undefined (actually it is at the first non-padding + * record that follows the message header record). + */ + if (ptr_offset != 0) { + rec_type = REC_TYPE_PTR; + curr_offset = ptr_offset; + vstring_strcpy(buf, STR(ptr_buf)); + } + *prec_type = rec_type; + } + if (msg_verbose) + msg_info("%s: index %ld name %s type %d offset %ld", + myname, (long) index, header_label ? + header_label : "(none)", rec_type, (long) curr_offset); + + CLEANUP_FIND_HEADER_RETURN(curr_offset); +} + +/* cleanup_find_header_end - find end of header */ + +static off_t cleanup_find_header_end(CLEANUP_STATE *state, + VSTRING *rec_buf, + int last_type) +{ + const char *myname = "cleanup_find_header_end"; + off_t read_offset; + int rec_type; + + /* + * This routine is called immediately after cleanup_find_header_start(). + * rec_buf is the cleanup_find_header_start() result record; last_type is + * the corresponding record type: REC_TYPE_PTR or REC_TYPE_NORM; the file + * read position is at the first non-padding record after the result + * header record. + */ + for (;;) { + if ((read_offset = vstream_ftell(state->dst)) < 0) { + msg_warn("%s: read file %s: %m", myname, cleanup_path); + cleanup_milter_error(state, errno); + return (-1); + } + /* Don't follow the "append header" pointer. */ + if (read_offset == state->append_hdr_pt_offset) + break; + /* Caution: this macro terminates the loop at end-of-message. */ + /* Don't do complex processing while breaking out of this loop. */ + GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, rec_buf, read_offset, + /* Warning and errno->error mapping are done elsewhere. */ + return (-1)); + if (rec_type == REC_TYPE_PTR) { + if (rec_goto(state->dst, STR(rec_buf)) < 0) { + msg_warn("%s: read file %s: %m", myname, cleanup_path); + cleanup_milter_error(state, errno); + return (-1); + } + /* Don't update last_type; PTR may follow REC_TYPE_CONT. */ + continue; + } + /* Start of header or message body. */ + if (last_type != REC_TYPE_CONT && !IS_SPACE_TAB(STR(rec_buf)[0])) + break; + last_type = rec_type; + } + return (read_offset); +} + +/* cleanup_patch_header - patch new header into an existing header */ + +static const char *cleanup_patch_header(CLEANUP_STATE *state, + const char *new_hdr_name, + const char *hdr_space, + const char *new_hdr_value, + off_t old_rec_offset, + int old_rec_type, + VSTRING *old_rec_buf, + off_t next_offset) +{ + const char *myname = "cleanup_patch_header"; + VSTRING *buf = vstring_alloc(100); + off_t new_hdr_offset; + +#define CLEANUP_PATCH_HEADER_RETURN(ret) do { \ + vstring_free(buf); \ + return (ret); \ + } while (0) + + if (msg_verbose) + msg_info("%s: \"%s\" \"%s\" at %ld", + myname, new_hdr_name, new_hdr_value, (long) old_rec_offset); + + /* + * Allocate space after the end of the queue file for the new header and + * optionally save an existing record to make room for a forward pointer + * record. If the saved record was not a PTR record, follow the saved + * record by a reverse pointer record that points to the record after the + * original location of the saved record. + * + * We update the queue file in a safe manner: save the new header and the + * existing records after the end of the queue file, write the reverse + * pointer, and only then overwrite the saved records with the forward + * pointer to the new header. + * + * old_rec_offset, old_rec_type, and old_rec_buf specify the record that we + * are about to overwrite with a pointer record. If the record needs to + * be saved (i.e. old_rec_type > 0), the buffer contains the data content + * of exactly one PTR or text record. + * + * next_offset specifies the record that follows the to-be-overwritten + * record. It is ignored when the to-be-saved record is a pointer record. + */ + + /* + * Return early when Milter header checks request that this header record + * be dropped. + */ + vstring_sprintf(buf, "%s:%s%s", new_hdr_name, hdr_space, new_hdr_value); + if (cleanup_milter_hbc_checks + && cleanup_milter_header_checks(state, buf) == 0) + CLEANUP_PATCH_HEADER_RETURN(0); + + /* + * Write the new header to a new location after the end of the queue + * file. + */ + if ((new_hdr_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + CLEANUP_PATCH_HEADER_RETURN(cleanup_milter_error(state, errno)); + } + /* XXX emit prepended header, then clear it. */ + cleanup_out_header(state, buf); /* Includes padding */ + if (msg_verbose > 1) + msg_info("%s: %ld: write %.*s", myname, (long) new_hdr_offset, + LEN(buf) > 30 ? 30 : (int) LEN(buf), STR(buf)); + + /* + * Optionally, save the existing text record or pointer record that will + * be overwritten with the forward pointer. Pad a short saved record to + * ensure that it, too, can be overwritten by a pointer. + */ + if (old_rec_type > 0) { + CLEANUP_OUT_BUF(state, old_rec_type, old_rec_buf); + if (LEN(old_rec_buf) < REC_TYPE_PTR_PAYL_SIZE) + rec_pad(state->dst, REC_TYPE_DTXT, + REC_TYPE_PTR_PAYL_SIZE - LEN(old_rec_buf)); + if (msg_verbose > 1) + msg_info("%s: write %.*s", myname, LEN(old_rec_buf) > 30 ? + 30 : (int) LEN(old_rec_buf), STR(old_rec_buf)); + } + + /* + * If the saved record wasn't a PTR record, write the reverse pointer + * after the saved records. A reverse pointer value of -1 means we were + * confused about what we were going to save. + */ + if (old_rec_type != REC_TYPE_PTR) { + if (next_offset < 0) + msg_panic("%s: bad reverse pointer %ld", + myname, (long) next_offset); + cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, + (long) next_offset); + if (msg_verbose > 1) + msg_info("%s: write PTR %ld", myname, (long) next_offset); + } + + /* + * Write the forward pointer over the old record. Generally, a pointer + * record will be shorter than a header record, so there will be a gap in + * the queue file before the next record. In other words, we must always + * follow pointer records otherwise we get out of sync with the data. + */ + if (vstream_fseek(state->dst, old_rec_offset, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + CLEANUP_PATCH_HEADER_RETURN(cleanup_milter_error(state, errno)); + } + cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, + (long) new_hdr_offset); + if (msg_verbose > 1) + msg_info("%s: %ld: write PTR %ld", myname, (long) old_rec_offset, + (long) new_hdr_offset); + + /* + * In case of error while doing record output. + */ + CLEANUP_PATCH_HEADER_RETURN( + CLEANUP_OUT_OK(state) == 0 ? cleanup_milter_error(state, 0) : + cleanup_milter_hbc_reply && LEN(cleanup_milter_hbc_reply) ? + STR(cleanup_milter_hbc_reply) : 0); + + /* + * Note: state->append_hdr_pt_target never changes. + */ +} + +/* cleanup_ins_header - insert message header */ + +static const char *cleanup_ins_header(void *context, ssize_t index, + const char *new_hdr_name, + const char *hdr_space, + const char *new_hdr_value) +{ + const char *myname = "cleanup_ins_header"; + CLEANUP_STATE *state = (CLEANUP_STATE *) context; + VSTRING *old_rec_buf = vstring_alloc(100); + off_t old_rec_offset; + int old_rec_type; + off_t next_offset; + const char *ret; + +#define CLEANUP_INS_HEADER_RETURN(ret) do { \ + vstring_free(old_rec_buf); \ + return (ret); \ + } while (0) + + if (msg_verbose) + msg_info("%s: %ld \"%s\" \"%s\"", + myname, (long) index, new_hdr_name, new_hdr_value); + + /* + * Look for a header at the specified position. + * + * The lookup result may be a pointer record. This allows us to make some + * optimization when multiple insert operations happen in the same place. + * + * Index 1 is the top-most header. + */ +#define NO_HEADER_NAME ((char *) 0) +#define ALLOW_PTR_BACKUP 1 +#define SKIP_ONE_HEADER 1 +#define DONT_SKIP_HEADERS 0 + + if (index < 1) + index = 1; + old_rec_offset = cleanup_find_header_start(state, index, NO_HEADER_NAME, + old_rec_buf, &old_rec_type, + ALLOW_PTR_BACKUP, + DONT_SKIP_HEADERS); + if (old_rec_offset == CLEANUP_FIND_HEADER_IOERROR) + /* Warning and errno->error mapping are done elsewhere. */ + CLEANUP_INS_HEADER_RETURN(cleanup_milter_error(state, 0)); + + /* + * If the header does not exist, simply append the header to the linked + * list at the "header append" pointer record. + */ + if (old_rec_offset < 0) + CLEANUP_INS_HEADER_RETURN(cleanup_add_header(context, new_hdr_name, + hdr_space, new_hdr_value)); + + /* + * If the header does exist, save both the new and the existing header to + * new storage at the end of the queue file, and link the new storage + * with a forward and reverse pointer (don't write a reverse pointer if + * we are starting with a pointer record). + */ + if (old_rec_type == REC_TYPE_PTR) { + next_offset = -1; + } else { + if ((next_offset = vstream_ftell(state->dst)) < 0) { + msg_warn("%s: read file %s: %m", myname, cleanup_path); + CLEANUP_INS_HEADER_RETURN(cleanup_milter_error(state, errno)); + } + } + ret = cleanup_patch_header(state, new_hdr_name, hdr_space, new_hdr_value, + old_rec_offset, old_rec_type, + old_rec_buf, next_offset); + CLEANUP_INS_HEADER_RETURN(ret); +} + +/* cleanup_upd_header - modify or append message header */ + +static const char *cleanup_upd_header(void *context, ssize_t index, + const char *new_hdr_name, + const char *hdr_space, + const char *new_hdr_value) +{ + const char *myname = "cleanup_upd_header"; + CLEANUP_STATE *state = (CLEANUP_STATE *) context; + VSTRING *rec_buf; + off_t old_rec_offset; + off_t next_offset; + int last_type; + const char *ret; + + if (msg_verbose) + msg_info("%s: %ld \"%s\" \"%s\"", + myname, (long) index, new_hdr_name, new_hdr_value); + + /* + * Sanity check. + */ + if (*new_hdr_name == 0) + msg_panic("%s: null header name", myname); + + /* + * Find the header that is being modified. + * + * The lookup result will never be a pointer record. + * + * Index 1 is the first matching header instance. + * + * XXX When a header is updated repeatedly we create jumps to jumps. To + * eliminate this, rewrite the loop below so that we can start with the + * pointer record that points to the header that's being edited. + */ +#define DONT_SAVE_RECORD 0 +#define NO_PTR_BACKUP 0 + +#define CLEANUP_UPD_HEADER_RETURN(ret) do { \ + vstring_free(rec_buf); \ + return (ret); \ + } while (0) + + rec_buf = vstring_alloc(100); + old_rec_offset = cleanup_find_header_start(state, index, new_hdr_name, + rec_buf, &last_type, + NO_PTR_BACKUP, + SKIP_ONE_HEADER); + if (old_rec_offset == CLEANUP_FIND_HEADER_IOERROR) + /* Warning and errno->error mapping are done elsewhere. */ + CLEANUP_UPD_HEADER_RETURN(cleanup_milter_error(state, 0)); + + /* + * If no old header is found, simply append the new header to the linked + * list at the "header append" pointer record. + */ + if (old_rec_offset < 0) + CLEANUP_UPD_HEADER_RETURN(cleanup_add_header(context, new_hdr_name, + hdr_space, new_hdr_value)); + + /* + * If the old header is found, find the end of the old header, save the + * new header to new storage at the end of the queue file, and link the + * new storage with a forward and reverse pointer. + */ + if ((next_offset = cleanup_find_header_end(state, rec_buf, last_type)) < 0) + /* Warning and errno->error mapping are done elsewhere. */ + CLEANUP_UPD_HEADER_RETURN(cleanup_milter_error(state, 0)); + ret = cleanup_patch_header(state, new_hdr_name, hdr_space, new_hdr_value, + old_rec_offset, DONT_SAVE_RECORD, + (VSTRING *) 0, next_offset); + CLEANUP_UPD_HEADER_RETURN(ret); +} + +/* cleanup_del_header - delete message header */ + +static const char *cleanup_del_header(void *context, ssize_t index, + const char *hdr_name) +{ + const char *myname = "cleanup_del_header"; + CLEANUP_STATE *state = (CLEANUP_STATE *) context; + VSTRING *rec_buf; + off_t header_offset; + off_t next_offset; + int last_type; + + if (msg_verbose) + msg_info("%s: %ld \"%s\"", myname, (long) index, hdr_name); + + /* + * Sanity check. + */ + if (*hdr_name == 0) + msg_panic("%s: null header name", myname); + + /* + * Find the header that is being deleted. + * + * The lookup result will never be a pointer record. + * + * Index 1 is the first matching header instance. + */ +#define CLEANUP_DEL_HEADER_RETURN(ret) do { \ + vstring_free(rec_buf); \ + return (ret); \ + } while (0) + + rec_buf = vstring_alloc(100); + header_offset = cleanup_find_header_start(state, index, hdr_name, rec_buf, + &last_type, NO_PTR_BACKUP, + SKIP_ONE_HEADER); + if (header_offset == CLEANUP_FIND_HEADER_IOERROR) + /* Warning and errno->error mapping are done elsewhere. */ + CLEANUP_DEL_HEADER_RETURN(cleanup_milter_error(state, 0)); + + /* + * Overwrite the beginning of the header record with a pointer to the + * information that follows the header. We can't simply overwrite the + * header with cleanup_out_header() and a special record type, because + * there may be a PTR record in the middle of a multi-line header. + */ + if (header_offset > 0) { + if ((next_offset = cleanup_find_header_end(state, rec_buf, last_type)) < 0) + /* Warning and errno->error mapping are done elsewhere. */ + CLEANUP_DEL_HEADER_RETURN(cleanup_milter_error(state, 0)); + /* Mark the header as deleted. */ + if (vstream_fseek(state->dst, header_offset, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + CLEANUP_DEL_HEADER_RETURN(cleanup_milter_error(state, errno)); + } + rec_fprintf(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, + (long) next_offset); + } + vstring_free(rec_buf); + + /* + * In case of error while doing record output. + */ + return (CLEANUP_OUT_OK(state) ? 0 : cleanup_milter_error(state, 0)); +} + +/* cleanup_chg_from - replace sender address, ignore ESMTP arguments */ + +static const char *cleanup_chg_from(void *context, const char *ext_from, + const char *esmtp_args) +{ + const char *myname = "cleanup_chg_from"; + CLEANUP_STATE *state = (CLEANUP_STATE *) context; + off_t new_offset; + off_t new_sender_offset; + off_t after_sender_offs; + int addr_count; + TOK822 *tree; + TOK822 *tp; + VSTRING *int_sender_buf; + int dsn_envid = 0; + int dsn_ret = 0; + + if (msg_verbose) + msg_info("%s: \"%s\" \"%s\"", myname, ext_from, esmtp_args); + + /* + * ESMTP support is limited to RET and ENVID, i.e. things that are stored + * together with the sender queue file record. + */ + if (esmtp_args[0]) { + ARGV *esmtp_argv; + int i; + const char *arg; + + esmtp_argv = argv_split(esmtp_args, " "); + for (i = 0; i < esmtp_argv->argc; ++i) { + arg = esmtp_argv->argv[i]; + if (strncasecmp(arg, "RET=", 4) == 0) { + if ((dsn_ret = dsn_ret_code(arg + 4)) == 0) { + msg_warn("Ignoring bad ESMTP parameter \"%s\" in " + "SMFI_CHGFROM request", arg); + } else { + state->dsn_ret = dsn_ret; + } + } else if (strncasecmp(arg, "ENVID=", 6) == 0) { + if (state->milter_dsn_buf == 0) + state->milter_dsn_buf = vstring_alloc(20); + dsn_envid = (xtext_unquote(state->milter_dsn_buf, arg + 6) + && allprint(STR(state->milter_dsn_buf))); + if (!dsn_envid) { + msg_warn("Ignoring bad ESMTP parameter \"%s\" in " + "SMFI_CHGFROM request", arg); + } else { + if (state->dsn_envid) + myfree(state->dsn_envid); + state->dsn_envid = mystrdup(STR(state->milter_dsn_buf)); + } + } else { + msg_warn("Ignoring bad ESMTP parameter \"%s\" in " + "SMFI_CHGFROM request", arg); + } + } + argv_free(esmtp_argv); + } + + /* + * The cleanup server remembers the file offset of the current sender + * address record (offset in sender_pt_offset) and the file offset of the + * record that follows the sender address (offset in sender_pt_target). + * Short original sender records are padded, so that they can safely be + * overwritten with a pointer record to the new sender address record. + */ + if (state->sender_pt_offset < 0) + msg_panic("%s: no original sender record offset", myname); + if (state->sender_pt_target < 0) + msg_panic("%s: no post-sender record offset", myname); + + /* + * Allocate space after the end of the queue file, and write the new {DSN + * envid, DSN ret, sender address, sender BCC} records, followed by a + * reverse pointer record that points to the record that follows the + * original sender record. + * + * We update the queue file in a safe manner: save the new sender after the + * end of the queue file, write the reverse pointer, and only then + * overwrite the old sender record with the forward pointer to the new + * sender. + */ + if ((new_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + return (cleanup_milter_error(state, errno)); + } + + /* + * Sender DSN attribute records precede the sender record. + */ + if (dsn_envid) + rec_fprintf(state->dst, REC_TYPE_ATTR, "%s=%s", + MAIL_ATTR_DSN_ENVID, STR(state->milter_dsn_buf)); + if (dsn_ret) + rec_fprintf(state->dst, REC_TYPE_ATTR, "%s=%d", + MAIL_ATTR_DSN_RET, dsn_ret); + if (dsn_envid == 0 && dsn_ret == 0) { + new_sender_offset = new_offset; + } else if ((new_sender_offset = vstream_ftell(state->dst)) < 0) { + msg_warn("%s: vstream_ftell file %s: %m", myname, cleanup_path); + return (cleanup_milter_error(state, errno)); + } + + /* + * Transform the address from external form to internal form. This also + * removes the enclosing <>, if present. + * + * XXX vstring_alloc() rejects zero-length requests. + */ + int_sender_buf = vstring_alloc(strlen(ext_from) + 1); + tree = tok822_parse(ext_from); + for (addr_count = 0, tp = tree; tp != 0; tp = tp->next) { + if (tp->type == TOK822_ADDR) { + if (addr_count == 0) { + tok822_internalize(int_sender_buf, tp->head, TOK822_STR_DEFL); + addr_count += 1; + } else { + msg_warn("%s: Milter request to add multi-sender: \"%s\"", + state->queue_id, ext_from); + break; + } + } + } + tok822_free_tree(tree); + after_sender_offs = cleanup_addr_sender(state, STR(int_sender_buf)); + vstring_free(int_sender_buf); + cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, + (long) state->sender_pt_target); + state->sender_pt_target = after_sender_offs; + + /* + * Overwrite the current sender record with the pointer to the new {DSN + * envid, DSN ret, sender address, sender BCC} records. + */ + if (vstream_fseek(state->dst, state->sender_pt_offset, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + return (cleanup_milter_error(state, errno)); + } + cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, + (long) new_offset); + + /* + * Remember the location of the new current sender record. + */ + state->sender_pt_offset = new_sender_offset; + + /* + * In case of error while doing record output. + */ + return (CLEANUP_OUT_OK(state) ? 0 : cleanup_milter_error(state, 0)); +} + +/* cleanup_add_rcpt_par - append recipient address, with ESMTP arguments */ + +static const char *cleanup_add_rcpt_par(void *context, const char *ext_rcpt, + const char *esmtp_args) +{ + const char *myname = "cleanup_add_rcpt_par"; + CLEANUP_STATE *state = (CLEANUP_STATE *) context; + off_t new_rcpt_offset; + off_t reverse_ptr_offset; + int addr_count; + TOK822 *tree; + TOK822 *tp; + VSTRING *int_rcpt_buf; + VSTRING *orcpt_buf = 0; + ARGV *esmtp_argv; + int dsn_notify = 0; + const char *dsn_orcpt_info = 0; + size_t type_len; + int i; + const char *arg; + const char *arg_val; + + if (msg_verbose) + msg_info("%s: \"%s\" \"%s\"", myname, ext_rcpt, esmtp_args); + + /* + * To simplify implementation, the cleanup server writes a dummy + * "recipient append" pointer record after the last recipient. We cache + * both the location and the target of the current "recipient append" + * pointer record. + */ + if (state->append_rcpt_pt_offset < 0) + msg_panic("%s: no recipient append pointer location", myname); + if (state->append_rcpt_pt_target < 0) + msg_panic("%s: no recipient append pointer target", myname); + + /* + * Allocate space after the end of the queue file, and write the + * recipient record, followed by a reverse pointer record that points to + * the target of the old "recipient append" pointer record. This reverse + * pointer record becomes the new "recipient append" pointer record. + * + * We update the queue file in a safe manner: save the new recipient after + * the end of the queue file, write the reverse pointer, and only then + * overwrite the old "recipient append" pointer with the forward pointer + * to the new recipient. + */ + if ((new_rcpt_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + return (cleanup_milter_error(state, errno)); + } + + /* + * Parse ESMTP parameters. XXX UTF8SMTP don't assume ORCPT is xtext. + */ + if (esmtp_args[0]) { + esmtp_argv = argv_split(esmtp_args, " "); + for (i = 0; i < esmtp_argv->argc; ++i) { + arg = esmtp_argv->argv[i]; + if (strncasecmp(arg, "NOTIFY=", 7) == 0) { /* RFC 3461 */ + if (dsn_notify || (dsn_notify = dsn_notify_mask(arg + 7)) == 0) + msg_warn("%s: Bad NOTIFY parameter from Milter or " + "header/body_checks: \"%.100s\"", + state->queue_id, arg); + } else if (strncasecmp(arg, "ORCPT=", 6) == 0) { /* RFC 3461 */ + if (orcpt_buf == 0) + orcpt_buf = vstring_alloc(100); + if (dsn_orcpt_info + || (type_len = strcspn(arg_val = arg + 6, ";")) == 0 + || (arg_val)[type_len] != ';' + || xtext_unquote_append(vstring_sprintf(orcpt_buf, + "%.*s;", (int) type_len, + arg_val), + arg_val + type_len + 1) == 0) { + msg_warn("%s: Bad ORCPT parameter from Milter or " + "header/body_checks: \"%.100s\"", + state->queue_id, arg); + } else { + dsn_orcpt_info = STR(orcpt_buf); + } + } else { + msg_warn("%s: ignoring ESMTP argument from Milter or " + "header/body_checks: \"%.100s\"", + state->queue_id, arg); + } + } + argv_free(esmtp_argv); + } + + /* + * Transform recipient from external form to internal form. This also + * removes the enclosing <>, if present. + * + * XXX vstring_alloc() rejects zero-length requests. + */ + int_rcpt_buf = vstring_alloc(strlen(ext_rcpt) + 1); + tree = tok822_parse(ext_rcpt); + for (addr_count = 0, tp = tree; tp != 0; tp = tp->next) { + if (tp->type == TOK822_ADDR) { + if (addr_count == 0) { + tok822_internalize(int_rcpt_buf, tp->head, TOK822_STR_DEFL); + addr_count += 1; + } else { + msg_warn("%s: Milter or header/body_checks request to " + "add multi-recipient: \"%s\"", + state->queue_id, ext_rcpt); + break; + } + } + } + tok822_free_tree(tree); + if (addr_count != 0) + cleanup_addr_bcc_dsn(state, STR(int_rcpt_buf), dsn_orcpt_info, + dsn_notify ? dsn_notify : DEF_DSN_NOTIFY); + else + msg_warn("%s: ignoring attempt from Milter to add null recipient", + state->queue_id); + vstring_free(int_rcpt_buf); + if (orcpt_buf) + vstring_free(orcpt_buf); + + /* + * Don't update the queue file when we did not write a recipient record + * (malformed or duplicate BCC recipient). + */ + if (vstream_ftell(state->dst) == new_rcpt_offset) + return (CLEANUP_OUT_OK(state) ? 0 : cleanup_milter_error(state, 0)); + + /* + * Follow the recipient with a "reverse" pointer to the old recipient + * append target. + */ + if ((reverse_ptr_offset = vstream_ftell(state->dst)) < 0) { + msg_warn("%s: vstream_ftell file %s: %m", myname, cleanup_path); + return (cleanup_milter_error(state, errno)); + } + cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, + (long) state->append_rcpt_pt_target); + + /* + * Pointer flipping: update the old "recipient append" pointer record + * value to the location of the new recipient record. + */ + if (vstream_fseek(state->dst, state->append_rcpt_pt_offset, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + return (cleanup_milter_error(state, errno)); + } + cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, + (long) new_rcpt_offset); + + /* + * Update the in-memory "recipient append" pointer record location with + * the location of the reverse pointer record that follows the new + * recipient. The target of the "recipient append" pointer record does + * not change; it's always the record that follows the dummy pointer + * record that was written while Postfix received the message. + */ + state->append_rcpt_pt_offset = reverse_ptr_offset; + + /* + * In case of error while doing record output. + */ + return (CLEANUP_OUT_OK(state) ? 0 : cleanup_milter_error(state, 0)); +} + +/* cleanup_add_rcpt - append recipient address */ + +static const char *cleanup_add_rcpt(void *context, const char *ext_rcpt) +{ + return (cleanup_add_rcpt_par(context, ext_rcpt, "")); +} + +/* cleanup_del_rcpt - remove recipient and all its expansions */ + +static const char *cleanup_del_rcpt(void *context, const char *ext_rcpt) +{ + const char *myname = "cleanup_del_rcpt"; + CLEANUP_STATE *state = (CLEANUP_STATE *) context; + off_t curr_offset; + VSTRING *buf; + char *attr_name; + char *attr_value; + char *dsn_orcpt = 0; /* XXX for dup filter cleanup */ + int dsn_notify = 0; /* XXX for dup filter cleanup */ + char *orig_rcpt = 0; + char *start; + int rec_type; + int junk; + int count = 0; + TOK822 *tree; + TOK822 *tp; + VSTRING *int_rcpt_buf; + int addr_count; + + if (msg_verbose) + msg_info("%s: \"%s\"", myname, ext_rcpt); + + /* + * Virtual aliasing and other address rewriting happens after the mail + * filter sees the envelope address. Therefore we must delete all + * recipient records whose Postfix (not DSN) original recipient address + * matches the specified address. + * + * As the number of recipients may be very large we can't do an efficient + * two-pass implementation (collect record offsets first, then mark + * records as deleted). Instead we mark records as soon as we find them. + * This is less efficient because we do (seek-write-read) for each marked + * recipient, instead of (seek-write). It's unlikely that VSTREAMs will + * be made smart enough to eliminate unnecessary I/O with small seeks. + * + * XXX When Postfix original recipients are turned off, we have no option + * but to match against the expanded and rewritten recipient address. + * + * XXX Remove the (dsn_orcpt, dsn_notify, orcpt, recip) tuple from the + * duplicate recipient filter. This requires that we maintain reference + * counts. + */ + if (vstream_fseek(state->dst, 0L, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + return (cleanup_milter_error(state, errno)); + } +#define CLEANUP_DEL_RCPT_RETURN(ret) do { \ + if (orig_rcpt != 0) \ + myfree(orig_rcpt); \ + if (dsn_orcpt != 0) \ + myfree(dsn_orcpt); \ + vstring_free(buf); \ + vstring_free(int_rcpt_buf); \ + return (ret); \ + } while (0) + + /* + * Transform recipient from external form to internal form. This also + * removes the enclosing <>, if present. + * + * XXX vstring_alloc() rejects zero-length requests. + */ + int_rcpt_buf = vstring_alloc(strlen(ext_rcpt) + 1); + tree = tok822_parse(ext_rcpt); + for (addr_count = 0, tp = tree; tp != 0; tp = tp->next) { + if (tp->type == TOK822_ADDR) { + if (addr_count == 0) { + tok822_internalize(int_rcpt_buf, tp->head, TOK822_STR_DEFL); + addr_count += 1; + } else { + msg_warn("%s: Milter request to drop multi-recipient: \"%s\"", + state->queue_id, ext_rcpt); + break; + } + } + } + tok822_free_tree(tree); + + buf = vstring_alloc(100); + for (;;) { + if (CLEANUP_OUT_OK(state) == 0) + /* Warning and errno->error mapping are done elsewhere. */ + CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, 0)); + if ((curr_offset = vstream_ftell(state->dst)) < 0) { + msg_warn("%s: vstream_ftell file %s: %m", myname, cleanup_path); + CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, errno)); + } + if ((rec_type = rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE)) <= 0) { + msg_warn("%s: read file %s: %m", myname, cleanup_path); + CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, errno)); + } + if (rec_type == REC_TYPE_END) + break; + /* Skip over message content. */ + if (rec_type == REC_TYPE_MESG) { + if (vstream_fseek(state->dst, state->xtra_offset, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, errno)); + } + continue; + } + start = STR(buf); + if (rec_type == REC_TYPE_PTR) { + if (rec_goto(state->dst, start) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, errno)); + } + continue; + } + /* Map attribute names to pseudo record type. */ + if (rec_type == REC_TYPE_ATTR) { + if (split_nameval(STR(buf), &attr_name, &attr_value) != 0 + || *attr_value == 0) + continue; + if ((junk = rec_attr_map(attr_name)) != 0) { + start = attr_value; + rec_type = junk; + } + } + switch (rec_type) { + case REC_TYPE_DSN_ORCPT: /* RCPT TO ORCPT parameter */ + if (dsn_orcpt != 0) /* can't happen */ + myfree(dsn_orcpt); + dsn_orcpt = mystrdup(start); + break; + case REC_TYPE_DSN_NOTIFY: /* RCPT TO NOTIFY parameter */ + if (alldig(start) && (junk = atoi(start)) > 0 + && DSN_NOTIFY_OK(junk)) + dsn_notify = junk; + else + dsn_notify = 0; + break; + case REC_TYPE_ORCP: /* unmodified RCPT TO address */ + if (orig_rcpt != 0) /* can't happen */ + myfree(orig_rcpt); + orig_rcpt = mystrdup(start); + break; + case REC_TYPE_RCPT: /* rewritten RCPT TO address */ + if (strcmp(orig_rcpt ? orig_rcpt : start, STR(int_rcpt_buf)) == 0) { + if (vstream_fseek(state->dst, curr_offset, SEEK_SET) < 0) { + msg_warn("%s: seek file %s: %m", myname, cleanup_path); + CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, errno)); + } + if (REC_PUT_BUF(state->dst, REC_TYPE_DRCP, buf) < 0) { + msg_warn("%s: write queue file: %m", state->queue_id); + CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, errno)); + } + count++; + } + if (var_enable_orcpt) + been_here_drop(state->dups, "%s\n%d\n%s\n%s", + dsn_orcpt ? dsn_orcpt : "", dsn_notify, + orig_rcpt ? orig_rcpt : "", STR(int_rcpt_buf)); + /* FALLTHROUGH */ + case REC_TYPE_DRCP: /* canceled recipient */ + case REC_TYPE_DONE: /* can't happen */ + if (orig_rcpt != 0) { + myfree(orig_rcpt); + orig_rcpt = 0; + } + if (dsn_orcpt != 0) { + myfree(dsn_orcpt); + dsn_orcpt = 0; + } + dsn_notify = 0; + break; + } + } + if (var_enable_orcpt == 0 && count > 0) + been_here_drop_fixed(state->dups, STR(int_rcpt_buf)); + + if (msg_verbose) + msg_info("%s: deleted %d records for recipient \"%s\"", + myname, count, ext_rcpt); + + CLEANUP_DEL_RCPT_RETURN(0); +} + +/* cleanup_repl_body - replace message body */ + +static const char *cleanup_repl_body(void *context, int cmd, int rec_type, + VSTRING *buf) +{ + const char *myname = "cleanup_repl_body"; + CLEANUP_STATE *state = (CLEANUP_STATE *) context; + static VSTRING empty; + + /* + * XXX Sendmail compatibility: milters don't see the first body line, so + * don't expect they will send one. + */ + switch (cmd) { + case MILTER_BODY_LINE: + if (cleanup_body_edit_write(state, rec_type, buf) < 0) + return (cleanup_milter_error(state, errno)); + break; + case MILTER_BODY_START: + VSTRING_RESET(&empty); + if (cleanup_body_edit_start(state) < 0 + || cleanup_body_edit_write(state, REC_TYPE_NORM, &empty) < 0) + return (cleanup_milter_error(state, errno)); + break; + case MILTER_BODY_END: + if (cleanup_body_edit_finish(state) < 0) + return (cleanup_milter_error(state, errno)); + break; + default: + msg_panic("%s: bad command: %d", myname, cmd); + } + return (CLEANUP_OUT_OK(state) ? 0 : cleanup_milter_error(state, errno)); +} + +/* cleanup_milter_eval - expand macro */ + +static const char *cleanup_milter_eval(const char *name, void *ptr) +{ + CLEANUP_STATE *state = (CLEANUP_STATE *) ptr; + + /* + * Note: if we use XFORWARD attributes here, then consistency requires + * that we forward all Sendmail macros via XFORWARD. + */ + + /* + * System macros. + */ + if (strcmp(name, S8_MAC_DAEMON_NAME) == 0) + return (var_milt_daemon_name); + if (strcmp(name, S8_MAC_V) == 0) + return (var_milt_v); + + /* + * Connect macros. + */ +#ifndef CLIENT_ATTR_UNKNOWN +#define CLIENT_ATTR_UNKNOWN "unknown" +#define SERVER_ATTR_UNKNOWN "unknown" +#endif + + if (strcmp(name, S8_MAC__) == 0) { + vstring_sprintf(state->temp1, "%s [%s]", + state->reverse_name, state->client_addr); + if (strcasecmp(state->client_name, state->reverse_name) != 0) + vstring_strcat(state->temp1, " (may be forged)"); + return (STR(state->temp1)); + } + if (strcmp(name, S8_MAC_J) == 0) + return (var_myhostname); + if (strcmp(name, S8_MAC_CLIENT_ADDR) == 0) + return (state->client_addr); + if (strcmp(name, S8_MAC_CLIENT_NAME) == 0) + return (state->client_name); + if (strcmp(name, S8_MAC_CLIENT_PORT) == 0) + return (state->client_port + && strcmp(state->client_port, CLIENT_ATTR_UNKNOWN) ? + state->client_port : "0"); + if (strcmp(name, S8_MAC_CLIENT_PTR) == 0) + return (state->reverse_name); + /* XXX S8_MAC_CLIENT_RES needs SMTPD_PEER_CODE_XXX from smtpd. */ + if (strcmp(name, S8_MAC_DAEMON_ADDR) == 0) + return (state->server_addr); + if (strcmp(name, S8_MAC_DAEMON_PORT) == 0) + return (state->server_port + && strcmp(state->server_port, SERVER_ATTR_UNKNOWN) ? + state->server_port : "0"); + + /* + * MAIL FROM macros. + */ + if (strcmp(name, S8_MAC_I) == 0) + return (state->queue_id); +#ifdef USE_SASL_AUTH + if (strcmp(name, S8_MAC_AUTH_TYPE) == 0) + return (nvtable_find(state->attr, MAIL_ATTR_SASL_METHOD)); + if (strcmp(name, S8_MAC_AUTH_AUTHEN) == 0) + return (nvtable_find(state->attr, MAIL_ATTR_SASL_USERNAME)); + if (strcmp(name, S8_MAC_AUTH_AUTHOR) == 0) + return (nvtable_find(state->attr, MAIL_ATTR_SASL_SENDER)); +#endif + if (strcmp(name, S8_MAC_MAIL_ADDR) == 0) + return (state->milter_ext_from ? STR(state->milter_ext_from) : 0); + + /* + * RCPT TO macros. + */ + if (strcmp(name, S8_MAC_RCPT_ADDR) == 0) + return (state->milter_ext_rcpt ? STR(state->milter_ext_rcpt) : 0); + return (0); +} + +/* cleanup_milter_receive - receive milter instances */ + +void cleanup_milter_receive(CLEANUP_STATE *state, int count) +{ + if (state->milters) + milter_free(state->milters); + state->milters = milter_receive(state->src, count); + if (state->milters == 0) + msg_fatal("cleanup_milter_receive: milter receive failed"); + if (count <= 0) + return; + milter_macro_callback(state->milters, cleanup_milter_eval, (void *) state); + milter_edit_callback(state->milters, + cleanup_add_header, cleanup_upd_header, + cleanup_ins_header, cleanup_del_header, + cleanup_chg_from, cleanup_add_rcpt, + cleanup_add_rcpt_par, cleanup_del_rcpt, + cleanup_repl_body, (void *) state); +} + +/* cleanup_milter_apply - apply Milter response, non-zero if rejecting */ + +static const char *cleanup_milter_apply(CLEANUP_STATE *state, const char *event, + const char *resp) +{ + const char *myname = "cleanup_milter_apply"; + const char *action; + const char *text; + const char *attr; + const char *ret = 0; + + if (msg_verbose) + msg_info("%s: %s", myname, resp); + + /* + * Don't process our own milter_header/body checks replies. See comments + * in cleanup_milter_hbc_extend(). + */ + if (cleanup_milter_hbc_reply && + strcmp(resp, STR(cleanup_milter_hbc_reply)) == 0) + return (0); + + /* + * Don't process Milter replies that are redundant because header/body + * checks already decided that we will not receive the message; or Milter + * replies that would have conflicting effect with the outcome of + * header/body checks (for example, header_checks "discard" action + * followed by Milter "reject" reply). Logging both actions would look + * silly. + */ + if (CLEANUP_MILTER_REJECTING_OR_DISCARDING_MESSAGE(state)) { + if (msg_verbose) + msg_info("%s: ignoring redundant or conflicting milter reply: %s", + state->queue_id, resp); + return (0); + } + + /* + * Sanity check. + */ + if (state->client_name == 0) + msg_panic("%s: missing client info initialization", myname); + + /* + * We don't report errors that were already reported by the content + * editing call-back routines. See cleanup_milter_error() above. + */ + if (CLEANUP_OUT_OK(state) == 0) + return (0); + switch (resp[0]) { + case 'H': + /* XXX Should log the reason here. */ + if (state->flags & CLEANUP_FLAG_HOLD) + return (0); + state->flags |= CLEANUP_FLAG_HOLD; + action = "milter-hold"; + text = "milter triggers HOLD action"; + break; + case 'D': + state->flags |= CLEANUP_FLAG_DISCARD; + action = "milter-discard"; + text = "milter triggers DISCARD action"; + break; + case 'S': + /* XXX Can this happen after end-of-message? */ + state->flags |= CLEANUP_STAT_CONT; + action = "milter-reject"; + text = cleanup_strerror(CLEANUP_STAT_CONT); + break; + + /* + * Override permanent reject with temporary reject. This happens when + * the cleanup server has to bounce (hard reject) but is unable to + * store the message (soft reject). After a temporary reject we stop + * inspecting queue file records, so it can't be overruled by + * something else. + * + * CLEANUP_STAT_CONT and CLEANUP_STAT_DEFER both update the reason + * attribute, but CLEANUP_STAT_DEFER takes precedence. It terminates + * queue record processing, and prevents bounces from being sent. + */ + case '4': + CLEANUP_MILTER_SET_SMTP_REPLY(state, resp); + ret = state->reason; + state->errs |= CLEANUP_STAT_DEFER; + action = "milter-reject"; + text = resp + 4; + break; + case '5': + CLEANUP_MILTER_SET_SMTP_REPLY(state, resp); + ret = state->reason; + state->errs |= CLEANUP_STAT_CONT; + action = "milter-reject"; + text = resp + 4; + break; + default: + msg_panic("%s: unexpected mail filter reply: %s", myname, resp); + } + vstring_sprintf(state->temp1, "%s: %s: %s from %s[%s]: %s;", + state->queue_id, action, event, state->client_name, + state->client_addr, text); + if (state->sender) + vstring_sprintf_append(state->temp1, " from=<%s>", + info_log_addr_form_sender(state->sender)); + if (state->recip) + vstring_sprintf_append(state->temp1, " to=<%s>", + info_log_addr_form_recipient(state->recip)); + if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_PROTO_NAME)) != 0) + vstring_sprintf_append(state->temp1, " proto=%s", attr); + if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_HELO_NAME)) != 0) + vstring_sprintf_append(state->temp1, " helo=<%s>", attr); + msg_info("%s", vstring_str(state->temp1)); + + return (ret); +} + +/* cleanup_milter_client_init - initialize real or ersatz client info */ + +static void cleanup_milter_client_init(CLEANUP_STATE *state) +{ + static INET_PROTO_INFO *proto_info; + const char *proto_attr; + + /* + * Either the cleanup client specifies a name, address and protocol, or + * we have a local submission and pretend localhost/127.0.0.1/AF_INET. + */ +#define NO_CLIENT_PORT "0" + + state->client_name = nvtable_find(state->attr, MAIL_ATTR_ACT_CLIENT_NAME); + state->reverse_name = + nvtable_find(state->attr, MAIL_ATTR_ACT_REVERSE_CLIENT_NAME); + state->client_addr = nvtable_find(state->attr, MAIL_ATTR_ACT_CLIENT_ADDR); + state->client_port = nvtable_find(state->attr, MAIL_ATTR_ACT_CLIENT_PORT); + proto_attr = nvtable_find(state->attr, MAIL_ATTR_ACT_CLIENT_AF); + state->server_addr = nvtable_find(state->attr, MAIL_ATTR_ACT_SERVER_ADDR); + state->server_port = nvtable_find(state->attr, MAIL_ATTR_ACT_SERVER_PORT); + + if (state->client_name == 0 || state->client_addr == 0 || proto_attr == 0 + || !alldig(proto_attr)) { + state->client_name = "localhost"; +#ifdef AF_INET6 + if (proto_info == 0) + proto_info = inet_proto_info(); + if (proto_info->sa_family_list[0] == PF_INET6) { + state->client_addr = "::1"; + state->client_af = AF_INET6; + } else +#endif + { + state->client_addr = "127.0.0.1"; + state->client_af = AF_INET; + } + state->server_addr = state->client_addr; + } else + state->client_af = atoi(proto_attr); + if (state->reverse_name == 0) + state->reverse_name = state->client_name; + /* Compatibility with pre-2.5 queue files. */ + if (state->client_port == 0) { + state->client_port = NO_CLIENT_PORT; + state->server_port = state->client_port; + } +} + +/* cleanup_milter_inspect - run message through mail filter */ + +void cleanup_milter_inspect(CLEANUP_STATE *state, MILTERS *milters) +{ + const char *myname = "cleanup_milter"; + const char *resp; + + if (msg_verbose) + msg_info("enter %s", myname); + + /* + * Initialize, in case we're called via smtpd(8). + */ + if (state->client_name == 0) + cleanup_milter_client_init(state); + + /* + * Prologue: prepare for Milter header/body checks. + */ + if (*var_milt_head_checks) + cleanup_milter_header_checks_reinit(state); + + /* + * Process mail filter replies. The reply format is verified by the mail + * filter library. + */ + if ((resp = milter_message(milters, state->handle->stream, + state->data_offset, state->auto_hdrs)) != 0) + cleanup_milter_apply(state, "END-OF-MESSAGE", resp); + + /* + * Epilogue: finalize Milter header/body checks. + */ + if (*var_milt_head_checks) + cleanup_milter_hbc_finish(state); + + if (msg_verbose) + msg_info("leave %s", myname); +} + +/* cleanup_milter_emul_mail - emulate connect/ehlo/mail event */ + +void cleanup_milter_emul_mail(CLEANUP_STATE *state, + MILTERS *milters, + const char *addr) +{ + const char *resp; + const char *helo; + const char *argv[2]; + + /* + * Per-connection initialization. + */ + milter_macro_callback(milters, cleanup_milter_eval, (void *) state); + milter_edit_callback(milters, + cleanup_add_header, cleanup_upd_header, + cleanup_ins_header, cleanup_del_header, + cleanup_chg_from, cleanup_add_rcpt, + cleanup_add_rcpt_par, cleanup_del_rcpt, + cleanup_repl_body, (void *) state); + if (state->client_name == 0) + cleanup_milter_client_init(state); + + /* + * Emulate SMTP events. + */ + if ((resp = milter_conn_event(milters, state->client_name, state->client_addr, + state->client_port, state->client_af)) != 0) { + cleanup_milter_apply(state, "CONNECT", resp); + return; + } +#define PRETEND_ESMTP 1 + + if (CLEANUP_MILTER_OK(state)) { + if ((helo = nvtable_find(state->attr, MAIL_ATTR_ACT_HELO_NAME)) == 0) + helo = state->client_name; + if ((resp = milter_helo_event(milters, helo, PRETEND_ESMTP)) != 0) { + cleanup_milter_apply(state, "EHLO", resp); + return; + } + } + if (CLEANUP_MILTER_OK(state)) { + if (state->milter_ext_from == 0) + state->milter_ext_from = vstring_alloc(100); + /* Sendmail 8.13 does not externalize the null address. */ + if (*addr) + quote_821_local(state->milter_ext_from, addr); + else + vstring_strcpy(state->milter_ext_from, addr); + argv[0] = STR(state->milter_ext_from); + argv[1] = 0; + if ((resp = milter_mail_event(milters, argv)) != 0) { + cleanup_milter_apply(state, "MAIL", resp); + return; + } + } +} + +/* cleanup_milter_emul_rcpt - emulate rcpt event */ + +void cleanup_milter_emul_rcpt(CLEANUP_STATE *state, + MILTERS *milters, + const char *addr) +{ + const char *myname = "cleanup_milter_emul_rcpt"; + const char *resp; + const char *argv[2]; + + /* + * Sanity check. + */ + if (state->client_name == 0) + msg_panic("%s: missing client info initialization", myname); + + /* + * CLEANUP_STAT_CONT and CLEANUP_STAT_DEFER both update the reason + * attribute, but CLEANUP_STAT_DEFER takes precedence. It terminates + * queue record processing, and prevents bounces from being sent. + */ + if (state->milter_ext_rcpt == 0) + state->milter_ext_rcpt = vstring_alloc(100); + /* Sendmail 8.13 does not externalize the null address. */ + if (*addr) + quote_821_local(state->milter_ext_rcpt, addr); + else + vstring_strcpy(state->milter_ext_rcpt, addr); + argv[0] = STR(state->milter_ext_rcpt); + argv[1] = 0; + if ((resp = milter_rcpt_event(milters, MILTER_FLAG_NONE, argv)) != 0 + && cleanup_milter_apply(state, "RCPT", resp) != 0) { + msg_warn("%s: milter configuration error: can't reject recipient " + "in non-smtpd(8) submission", state->queue_id); + msg_warn("%s: message not accepted, try again later", state->queue_id); + CLEANUP_MILTER_SET_REASON(state, "4.3.5 Server configuration error"); + state->errs |= CLEANUP_STAT_DEFER; + } +} + +/* cleanup_milter_emul_data - emulate data event */ + +void cleanup_milter_emul_data(CLEANUP_STATE *state, MILTERS *milters) +{ + const char *myname = "cleanup_milter_emul_data"; + const char *resp; + + /* + * Sanity check. + */ + if (state->client_name == 0) + msg_panic("%s: missing client info initialization", myname); + + if ((resp = milter_data_event(milters)) != 0) + cleanup_milter_apply(state, "DATA", resp); +} + +#ifdef TEST + + /* + * Queue file editing driver for regression tests. In this case it is OK to + * report fatal errors after I/O errors. + */ +#include <stdio.h> +#include <msg_vstream.h> +#include <vstring_vstream.h> +#include <mail_addr.h> +#include <mail_version.h> + +#undef msg_verbose + +char *cleanup_path; +VSTRING *cleanup_trace_path; +VSTRING *cleanup_strip_chars; +int cleanup_comm_canon_flags; +MAPS *cleanup_comm_canon_maps; +int cleanup_ext_prop_mask; +ARGV *cleanup_masq_domains; +int cleanup_masq_flags; +MAPS *cleanup_rcpt_bcc_maps; +int cleanup_rcpt_canon_flags; +MAPS *cleanup_rcpt_canon_maps; +MAPS *cleanup_send_bcc_maps; +int cleanup_send_canon_flags; +MAPS *cleanup_send_canon_maps; +int var_dup_filter_limit = DEF_DUP_FILTER_LIMIT; +char *var_empty_addr = DEF_EMPTY_ADDR; +MAPS *cleanup_virt_alias_maps; +char *var_milt_daemon_name = "host.example.com"; +char *var_milt_v = DEF_MILT_V; +MILTERS *cleanup_milters = (MILTERS *) ((char *) sizeof(*cleanup_milters)); +char *var_milt_head_checks = ""; + +/* Dummies to satisfy unused external references. */ + +int cleanup_masquerade_internal(CLEANUP_STATE *state, VSTRING *addr, ARGV *masq_domains) +{ + msg_panic("cleanup_masquerade_internal dummy"); +} + +int cleanup_rewrite_internal(const char *context, VSTRING *result, + const char *addr) +{ + vstring_strcpy(result, addr); + return (0); +} + +int cleanup_map11_internal(CLEANUP_STATE *state, VSTRING *addr, + MAPS *maps, int propagate) +{ + msg_panic("cleanup_map11_internal dummy"); +} + +ARGV *cleanup_map1n_internal(CLEANUP_STATE *state, const char *addr, + MAPS *maps, int propagate) +{ + msg_panic("cleanup_map1n_internal dummy"); +} + +void cleanup_envelope(CLEANUP_STATE *state, int type, const char *buf, + ssize_t len) +{ + msg_panic("cleanup_envelope dummy"); +} + +static void usage(void) +{ + msg_warn("usage:"); + msg_warn(" verbose on|off"); + msg_warn(" open pathname"); + msg_warn(" close"); + msg_warn(" add_header index name [value]"); + msg_warn(" ins_header index name [value]"); + msg_warn(" upd_header index name [value]"); + msg_warn(" del_header index name"); + msg_warn(" chg_from addr parameters"); + msg_warn(" add_rcpt addr"); + msg_warn(" add_rcpt_par addr parameters"); + msg_warn(" del_rcpt addr"); + msg_warn(" replbody pathname"); + msg_warn(" header_checks type:name"); +} + +/* flatten_args - unparse partial command line */ + +static void flatten_args(VSTRING *buf, char **argv) +{ + char **cpp; + + VSTRING_RESET(buf); + for (cpp = argv; *cpp; cpp++) { + vstring_strcat(buf, *cpp); + if (cpp[1]) + VSTRING_ADDCH(buf, ' '); + } + VSTRING_TERMINATE(buf); +} + +/* open_queue_file - open an unedited queue file (all-zero dummy PTRs) */ + +static void open_queue_file(CLEANUP_STATE *state, const char *path) +{ + VSTRING *buf = vstring_alloc(100); + off_t curr_offset; + int rec_type; + long msg_seg_len; + long data_offset; + long rcpt_count; + long qmgr_opts; + + if (state->dst != 0) { + msg_warn("closing %s", cleanup_path); + vstream_fclose(state->dst); + state->dst = 0; + myfree(cleanup_path); + cleanup_path = 0; + } + if ((state->dst = vstream_fopen(path, O_RDWR, 0)) == 0) { + msg_warn("open %s: %m", path); + } else { + cleanup_path = mystrdup(path); + for (;;) { + if ((curr_offset = vstream_ftell(state->dst)) < 0) + msg_fatal("file %s: vstream_ftell: %m", cleanup_path); + if ((rec_type = rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE)) < 0) + msg_fatal("file %s: missing SIZE or PTR record", cleanup_path); + if (rec_type == REC_TYPE_SIZE) { + if (sscanf(STR(buf), "%ld %ld %ld %ld", + &msg_seg_len, &data_offset, + &rcpt_count, &qmgr_opts) != 4) + msg_fatal("file %s: bad SIZE record: %s", + cleanup_path, STR(buf)); + state->data_offset = data_offset; + state->xtra_offset = data_offset + msg_seg_len; + } else if (rec_type == REC_TYPE_FROM) { + state->sender_pt_offset = curr_offset; + if (LEN(buf) < REC_TYPE_PTR_PAYL_SIZE + && rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE) != REC_TYPE_PTR) + msg_fatal("file %s: missing PTR record after short sender", + cleanup_path); + if ((state->sender_pt_target = vstream_ftell(state->dst)) < 0) + msg_fatal("file %s: missing END record", cleanup_path); + } else if (rec_type == REC_TYPE_PTR) { + if (state->data_offset < 0) + msg_fatal("file %s: missing SIZE record", cleanup_path); + if (curr_offset < state->data_offset + || curr_offset > state->xtra_offset) { + if (state->append_rcpt_pt_offset < 0) { + state->append_rcpt_pt_offset = curr_offset; + if (atol(STR(buf)) != 0) + msg_fatal("file %s: bad dummy recipient PTR record: %s", + cleanup_path, STR(buf)); + if ((state->append_rcpt_pt_target = + vstream_ftell(state->dst)) < 0) + msg_fatal("file %s: vstream_ftell: %m", cleanup_path); + } else if (curr_offset > state->xtra_offset + && state->append_meta_pt_offset < 0) { + state->append_meta_pt_offset = curr_offset; + if (atol(STR(buf)) != 0) + msg_fatal("file %s: bad dummy meta PTR record: %s", + cleanup_path, STR(buf)); + if ((state->append_meta_pt_target = + vstream_ftell(state->dst)) < 0) + msg_fatal("file %s: vstream_ftell: %m", cleanup_path); + } + } else { + if (state->append_hdr_pt_offset < 0) { + state->append_hdr_pt_offset = curr_offset; + if (atol(STR(buf)) != 0) + msg_fatal("file %s: bad dummy header PTR record: %s", + cleanup_path, STR(buf)); + if ((state->append_hdr_pt_target = + vstream_ftell(state->dst)) < 0) + msg_fatal("file %s: vstream_ftell: %m", cleanup_path); + } + } + } + if (state->append_rcpt_pt_offset > 0 + && state->append_hdr_pt_offset > 0 + && (rec_type == REC_TYPE_END + || state->append_meta_pt_offset > 0)) + break; + } + if (msg_verbose) { + msg_info("append_rcpt_pt_offset %ld append_rcpt_pt_target %ld", + (long) state->append_rcpt_pt_offset, + (long) state->append_rcpt_pt_target); + msg_info("append_hdr_pt_offset %ld append_hdr_pt_target %ld", + (long) state->append_hdr_pt_offset, + (long) state->append_hdr_pt_target); + } + } + vstring_free(buf); +} + +static void close_queue_file(CLEANUP_STATE *state) +{ + (void) vstream_fclose(state->dst); + state->dst = 0; + myfree(cleanup_path); + cleanup_path = 0; +} + +int main(int unused_argc, char **argv) +{ + VSTRING *inbuf = vstring_alloc(100); + VSTRING *arg_buf = vstring_alloc(100); + char *bufp; + int istty = isatty(vstream_fileno(VSTREAM_IN)); + CLEANUP_STATE *state = cleanup_state_alloc((VSTREAM *) 0); + const char *parens = "{}"; + + state->queue_id = mystrdup("NOQUEUE"); + state->sender = mystrdup("sender"); + state->recip = mystrdup("recipient"); + state->client_name = "client_name"; + state->client_addr = "client_addr"; + state->flags |= CLEANUP_FLAG_FILTER_ALL; + + msg_vstream_init(argv[0], VSTREAM_ERR); + var_line_limit = DEF_LINE_LIMIT; + var_header_limit = DEF_HEADER_LIMIT; + var_enable_orcpt = DEF_ENABLE_ORCPT; + var_info_log_addr_form = DEF_INFO_LOG_ADDR_FORM; + + for (;;) { + ARGV *argv; + ssize_t index; + const char *resp = 0; + + if (istty) { + vstream_printf("- "); + vstream_fflush(VSTREAM_OUT); + } + if (vstring_fgets_nonl(inbuf, VSTREAM_IN) == 0) + break; + + bufp = vstring_str(inbuf); + if (!istty) { + vstream_printf("> %s\n", bufp); + vstream_fflush(VSTREAM_OUT); + } + if (*bufp == '#' || *bufp == 0 || allspace(bufp)) + continue; + argv = argv_splitq(bufp, " ", parens); + if (argv->argc == 0) { + msg_warn("missing command"); + } else if (strcmp(argv->argv[0], "?") == 0) { + usage(); + } else if (strcmp(argv->argv[0], "verbose") == 0) { + if (argv->argc != 2) { + msg_warn("bad verbose argument count: %ld", (long) argv->argc); + } else if (strcmp(argv->argv[1], "on") == 0) { + msg_verbose = 2; + } else if (strcmp(argv->argv[1], "off") == 0) { + msg_verbose = 0; + } else { + msg_warn("bad verbose argument"); + } + } else if (strcmp(argv->argv[0], "open") == 0) { + if (state->dst != 0) { + msg_info("closing %s", VSTREAM_PATH(state->dst)); + close_queue_file(state); + } + if (argv->argc != 2) { + msg_warn("bad open argument count: %ld", (long) argv->argc); + } else { + open_queue_file(state, argv->argv[1]); + } + } else if (state->dst == 0) { + msg_warn("no open queue file"); + } else if (strcmp(argv->argv[0], "close") == 0) { + if (*var_milt_head_checks) { + cleanup_milter_hbc_finish(state); + myfree(var_milt_head_checks); + var_milt_head_checks = ""; + cleanup_milter_header_checks_deinit(); + } + close_queue_file(state); + } else if (cleanup_milter_hbc_reply && LEN(cleanup_milter_hbc_reply)) { + /* Postfix libmilter would skip further requests. */ + msg_info("ignoring: %s %s %s", argv->argv[0], + argv->argc > 1 ? argv->argv[1] : "", + argv->argc > 2 ? argv->argv[2] : ""); + } else if (strcmp(argv->argv[0], "add_header") == 0) { + if (argv->argc < 2) { + msg_warn("bad add_header argument count: %ld", + (long) argv->argc); + } else { + flatten_args(arg_buf, argv->argv + 2); + resp = cleanup_add_header(state, argv->argv[1], " ", STR(arg_buf)); + } + } else if (strcmp(argv->argv[0], "ins_header") == 0) { + if (argv->argc < 3) { + msg_warn("bad ins_header argument count: %ld", + (long) argv->argc); + } else if ((index = atoi(argv->argv[1])) < 1) { + msg_warn("bad ins_header index value"); + } else { + flatten_args(arg_buf, argv->argv + 3); + resp = cleanup_ins_header(state, index, argv->argv[2], " ", STR(arg_buf)); + } + } else if (strcmp(argv->argv[0], "upd_header") == 0) { + if (argv->argc < 3) { + msg_warn("bad upd_header argument count: %ld", + (long) argv->argc); + } else if ((index = atoi(argv->argv[1])) < 1) { + msg_warn("bad upd_header index value"); + } else { + flatten_args(arg_buf, argv->argv + 3); + resp = cleanup_upd_header(state, index, argv->argv[2], " ", STR(arg_buf)); + } + } else if (strcmp(argv->argv[0], "del_header") == 0) { + if (argv->argc != 3) { + msg_warn("bad del_header argument count: %ld", + (long) argv->argc); + } else if ((index = atoi(argv->argv[1])) < 1) { + msg_warn("bad del_header index value"); + } else { + cleanup_del_header(state, index, argv->argv[2]); + } + } else if (strcmp(argv->argv[0], "chg_from") == 0) { + if (argv->argc != 3) { + msg_warn("bad chg_from argument count: %ld", (long) argv->argc); + } else { + char *arg = argv->argv[2]; + const char *err; + + if (*arg == parens[0] + && (err = extpar(&arg, parens, EXTPAR_FLAG_NONE)) != 0) { + msg_warn("%s in \"%s\"", err, arg); + } else { + cleanup_chg_from(state, argv->argv[1], arg); + } + } + } else if (strcmp(argv->argv[0], "add_rcpt") == 0) { + if (argv->argc != 2) { + msg_warn("bad add_rcpt argument count: %ld", (long) argv->argc); + } else { + cleanup_add_rcpt(state, argv->argv[1]); + } + } else if (strcmp(argv->argv[0], "add_rcpt_par") == 0) { + if (argv->argc != 3) { + msg_warn("bad add_rcpt_par argument count: %ld", + (long) argv->argc); + } else { + cleanup_add_rcpt_par(state, argv->argv[1], argv->argv[2]); + } + } else if (strcmp(argv->argv[0], "del_rcpt") == 0) { + if (argv->argc != 2) { + msg_warn("bad del_rcpt argument count: %ld", (long) argv->argc); + } else { + cleanup_del_rcpt(state, argv->argv[1]); + } + } else if (strcmp(argv->argv[0], "replbody") == 0) { + if (argv->argc != 2) { + msg_warn("bad replbody argument count: %ld", (long) argv->argc); + } else { + VSTREAM *fp; + VSTRING *buf; + + if ((fp = vstream_fopen(argv->argv[1], O_RDONLY, 0)) == 0) { + msg_warn("open %s file: %m", argv->argv[1]); + } else { + buf = vstring_alloc(100); + cleanup_repl_body(state, MILTER_BODY_START, buf); + while (vstring_get_nonl(buf, fp) != VSTREAM_EOF) + cleanup_repl_body(state, MILTER_BODY_LINE, buf); + cleanup_repl_body(state, MILTER_BODY_END, buf); + vstring_free(buf); + vstream_fclose(fp); + } + } + } else if (strcmp(argv->argv[0], "header_checks") == 0) { + if (argv->argc != 2) { + msg_warn("bad header_checks argument count: %ld", + (long) argv->argc); + } else if (*var_milt_head_checks) { + msg_warn("can't change header checks"); + } else { + var_milt_head_checks = mystrdup(argv->argv[1]); + cleanup_milter_header_checks_init(); + } + } else if (strcmp(argv->argv[0], "sender_bcc_maps") == 0) { + if (argv->argc != 2) { + msg_warn("bad sender_bcc_maps argument count: %ld", + (long) argv->argc); + } else { + if (cleanup_send_bcc_maps) + maps_free(cleanup_send_bcc_maps); + cleanup_send_bcc_maps = + maps_create("sender_bcc_maps", argv->argv[1], + DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX + | DICT_FLAG_UTF8_REQUEST); + state->flags |= CLEANUP_FLAG_BCC_OK; + var_rcpt_delim = ""; + } + } else { + msg_warn("bad command: %s", argv->argv[0]); + } + argv_free(argv); + if (resp) + cleanup_milter_apply(state, "END-OF-MESSAGE", resp); + } + vstring_free(inbuf); + vstring_free(arg_buf); + if (state->append_meta_pt_offset >= 0) { + if (state->flags) + msg_info("flags = %s", cleanup_strflags(state->flags)); + if (state->errs) + msg_info("errs = %s", cleanup_strerror(state->errs)); + } + cleanup_state_free(state); + if (*var_milt_head_checks) + myfree(var_milt_head_checks); + + return (0); +} + +#endif diff --git a/src/cleanup/cleanup_milter.in1 b/src/cleanup/cleanup_milter.in1 new file mode 100644 index 0000000..85cc3ea --- /dev/null +++ b/src/cleanup/cleanup_milter.in1 @@ -0,0 +1,42 @@ +#verbose on +open test-queue-file.tmp + +# Add and remove some recipient records. We verify that all results +# from virtual alias expansion are deleted. We don't yet attempt to +# remove non-existent recipients. + +add_rcpt xxxx +add_rcpt yyyy +del_rcpt alias@hades.porcupine.org +del_rcpt yyyy + +# Insert a short header X2 at the position of a short multi-line +# header X, so that the first part of the multi-line header X needs +# to be copied to the heap, right after the inserted header. Then +# update the inserted header X2, so that a smaller portion of the +# saved multi-line header X needs to be copied again. Thus we end +# up with a multi-line header X that is broken up into three pieces. +# Finally, delete the inserted header X2. All this tests if an insert +# operation properly saves a portion of a multi-line header, to make +# space for the forward pointer to the inserted content. + +ins_header 2 X2 v1 +upd_header 1 X2 v2 +del_header 1 X2 + +# Insert a header at the position of a single-line short header Y, +# so that both header Y, and the single-line Message-ID header that +# follows Y, need to be copied to the heap. This tests if an insert +# operation properly saves records to make space for the forward +# pointer record to the inserted content. + +ins_header 3 X2 test header value 3 + +# Update the multiply broken, multi-line, header X. This tests if +# we correcly link the new header to the header that comes after the +# modified header. + +upd_header 1 X X-replaced-header replacement header text +#upd_header 1 X X-replaced-header replacement header text +#upd_header 1 X X-replaced-header replacement header text +close diff --git a/src/cleanup/cleanup_milter.in10a b/src/cleanup/cleanup_milter.in10a new file mode 100644 index 0000000..997b8a7 --- /dev/null +++ b/src/cleanup/cleanup_milter.in10a @@ -0,0 +1,11 @@ +# +# Replace a short body by a longer one. The result is two body +# regions: the original region with the head of the new text, and +# one region at the end of the queue file with the remainder of the +# new text. +# +open test-queue-file10.tmp + +replbody loremipsum + +close diff --git a/src/cleanup/cleanup_milter.in10b b/src/cleanup/cleanup_milter.in10b new file mode 100644 index 0000000..ba3b07f --- /dev/null +++ b/src/cleanup/cleanup_milter.in10b @@ -0,0 +1,12 @@ +# +# Replace a short body by a longer one, and then replace the longer +# body by itself. The result should be identical to what we had after +# one replacement. +# +#verbose on +open test-queue-file10.tmp + +replbody loremipsum +replbody loremipsum + +close diff --git a/src/cleanup/cleanup_milter.in10c b/src/cleanup/cleanup_milter.in10c new file mode 100644 index 0000000..79af0ce --- /dev/null +++ b/src/cleanup/cleanup_milter.in10c @@ -0,0 +1,14 @@ +# +# Replace a short body by a longer one, and then clobber that with +# an even longer body. The result is three body regions: the original +# region, the region that contained the tail of the first replacement, +# and a region that contains the tail of the second replacement. +# + +#verbose on +open test-queue-file10.tmp + +replbody loremipsum +replbody loremipsum2 + +close diff --git a/src/cleanup/cleanup_milter.in10d b/src/cleanup/cleanup_milter.in10d new file mode 100644 index 0000000..b7c515a --- /dev/null +++ b/src/cleanup/cleanup_milter.in10d @@ -0,0 +1,14 @@ +# +# As test 10c, but then replace the longer body by the shorter one. The +# result is three regions: the original region, the region with the +# tail of the shorter replacement, and an unused region that contained +# the tail of the larger region. +# +#verbose on +open test-queue-file10.tmp + +replbody loremipsum +replbody loremipsum2 +replbody loremipsum + +close diff --git a/src/cleanup/cleanup_milter.in10e b/src/cleanup/cleanup_milter.in10e new file mode 100644 index 0000000..763f79b --- /dev/null +++ b/src/cleanup/cleanup_milter.in10e @@ -0,0 +1,13 @@ +# +# Replace a short body by increasingly longer ones and do header +# updates in between. +# +#verbose on +open test-queue-file10.tmp + +add_header foo1 foobar +replbody loremipsum +add_header foo2 foobar +replbody loremipsum2 + +close diff --git a/src/cleanup/cleanup_milter.in11 b/src/cleanup/cleanup_milter.in11 new file mode 100644 index 0000000..8e69f71 --- /dev/null +++ b/src/cleanup/cleanup_milter.in11 @@ -0,0 +1,10 @@ +# +# Replace a non-existent body by a non-empty one. +# +#verbose on +open test-queue-file11.tmp + +replbody loremipsum +replbody loremipsum + +close diff --git a/src/cleanup/cleanup_milter.in12 b/src/cleanup/cleanup_milter.in12 new file mode 100644 index 0000000..bf44e60 --- /dev/null +++ b/src/cleanup/cleanup_milter.in12 @@ -0,0 +1,22 @@ +#verbose on +open test-queue-file12.tmp + +# Add a recipient to a message that was received with "sendmail -t" +# so that all the recipients are in the extracted queue file segment. + +add_rcpt me@porcupine.org + +# Delete the recipient added above. + +del_rcpt me@porcupine.org + +# Add a new recipient, using a different address than above, so that +# the duplicate filter won't suppress it. + +add_rcpt em@porcupine.org + +# Delete the recipient. + +del_rcpt em@porcupine.org + +close diff --git a/src/cleanup/cleanup_milter.in13a b/src/cleanup/cleanup_milter.in13a new file mode 100644 index 0000000..ab0f531 --- /dev/null +++ b/src/cleanup/cleanup_milter.in13a @@ -0,0 +1,22 @@ +#verbose on +open test-queue-file13a.tmp + +# Add a recipient to a message that was received with "sendmail -t" +# so that all the recipients are in the extracted queue file segment. + +add_rcpt_par me@porcupine.org esmtpstuff + +# Delete the recipient added above. + +del_rcpt me@porcupine.org + +# Add a new recipient, using a different address than above, so that +# the duplicate filter won't suppress it. + +add_rcpt_par em@porcupine.org esmtpstuff + +# Delete the recipient. + +del_rcpt em@porcupine.org + +close diff --git a/src/cleanup/cleanup_milter.in13b b/src/cleanup/cleanup_milter.in13b new file mode 100644 index 0000000..04ef9e2 --- /dev/null +++ b/src/cleanup/cleanup_milter.in13b @@ -0,0 +1,8 @@ +#verbose on +open test-queue-file13b.tmp + +# Change the sender. + +chg_from m@porcupine.org esmtpstuff + +close diff --git a/src/cleanup/cleanup_milter.in13c b/src/cleanup/cleanup_milter.in13c new file mode 100644 index 0000000..8bfa292 --- /dev/null +++ b/src/cleanup/cleanup_milter.in13c @@ -0,0 +1,9 @@ +#verbose on +open test-queue-file13c.tmp + +# Change the sender. + +chg_from m@porcupine.org esmtpstuff +chg_from n@porcupine.org esmtpstuff + +close diff --git a/src/cleanup/cleanup_milter.in13d b/src/cleanup/cleanup_milter.in13d new file mode 100644 index 0000000..da673fe --- /dev/null +++ b/src/cleanup/cleanup_milter.in13d @@ -0,0 +1,9 @@ +#verbose on +open test-queue-file13d.tmp + +# Change the null sender, to test correct padding of short sender records. + +chg_from m@porcupine.org esmtpstuff +chg_from n@porcupine.org esmtpstuff + +close diff --git a/src/cleanup/cleanup_milter.in13e b/src/cleanup/cleanup_milter.in13e new file mode 100644 index 0000000..a657c3c --- /dev/null +++ b/src/cleanup/cleanup_milter.in13e @@ -0,0 +1,10 @@ +#verbose on +open test-queue-file13e.tmp + +# Change the sender. + +sender_bcc_maps static:a@porcupine.org +chg_from m@porcupine.org esmtpstuff +#chg_from n@porcupine.org esmtpstuff + +close diff --git a/src/cleanup/cleanup_milter.in13f b/src/cleanup/cleanup_milter.in13f new file mode 100644 index 0000000..aeb7f5a --- /dev/null +++ b/src/cleanup/cleanup_milter.in13f @@ -0,0 +1,10 @@ +#verbose on +open test-queue-file13f.tmp + +# Change the sender. + +sender_bcc_maps static:a@porcupine.org +chg_from m@porcupine.org esmtpstuff +chg_from n@porcupine.org esmtpstuff + +close diff --git a/src/cleanup/cleanup_milter.in13g b/src/cleanup/cleanup_milter.in13g new file mode 100644 index 0000000..c88e3c9 --- /dev/null +++ b/src/cleanup/cleanup_milter.in13g @@ -0,0 +1,11 @@ +#verbose on +open test-queue-file13g.tmp + +# Change the sender. + +sender_bcc_maps static:a@porcupine.org +chg_from m@porcupine.org esmtpstuff +chg_from n@porcupine.org esmtpstuff +chg_from o@porcupine.org esmtpstuff + +close diff --git a/src/cleanup/cleanup_milter.in13h b/src/cleanup/cleanup_milter.in13h new file mode 100644 index 0000000..b5af323 --- /dev/null +++ b/src/cleanup/cleanup_milter.in13h @@ -0,0 +1,8 @@ +#verbose on +open test-queue-file13h.tmp + +# Change the sender. + +chg_from m@porcupine.org { ret=hdrs envid=env-for-m } + +close diff --git a/src/cleanup/cleanup_milter.in13i b/src/cleanup/cleanup_milter.in13i new file mode 100644 index 0000000..85dfeb0 --- /dev/null +++ b/src/cleanup/cleanup_milter.in13i @@ -0,0 +1,9 @@ +#verbose on +open test-queue-file13i.tmp + +# Change the sender. + +chg_from m@porcupine.org { ret=hdrs envid=env-for-m } +chg_from n@porcupine.org { ret=full envid=env-for-n } + +close diff --git a/src/cleanup/cleanup_milter.in14a b/src/cleanup/cleanup_milter.in14a new file mode 100644 index 0000000..6fc21f5 --- /dev/null +++ b/src/cleanup/cleanup_milter.in14a @@ -0,0 +1,7 @@ +#verbose on +open test-queue-file14a.tmp + +header_checks regexp:cleanup_milter.reg14a +add_header X-SPAM-FLAG YES + +close diff --git a/src/cleanup/cleanup_milter.in14b b/src/cleanup/cleanup_milter.in14b new file mode 100644 index 0000000..539112c --- /dev/null +++ b/src/cleanup/cleanup_milter.in14b @@ -0,0 +1,7 @@ +#verbose on +open test-queue-file14b.tmp + +header_checks regexp:cleanup_milter.reg14b +add_header X-SPAM-FLAG YES + +close diff --git a/src/cleanup/cleanup_milter.in14c b/src/cleanup/cleanup_milter.in14c new file mode 100644 index 0000000..0a247b2 --- /dev/null +++ b/src/cleanup/cleanup_milter.in14c @@ -0,0 +1,7 @@ +#verbose on +open test-queue-file14c.tmp + +header_checks regexp:cleanup_milter.reg14c +add_header X-SPAM-FLAG YES + +close diff --git a/src/cleanup/cleanup_milter.in14d b/src/cleanup/cleanup_milter.in14d new file mode 100644 index 0000000..13aa2ef --- /dev/null +++ b/src/cleanup/cleanup_milter.in14d @@ -0,0 +1,7 @@ +#verbose on +open test-queue-file14d.tmp + +header_checks regexp:cleanup_milter.reg14d +add_header X-SPAM-FLAG YES + +close diff --git a/src/cleanup/cleanup_milter.in14e b/src/cleanup/cleanup_milter.in14e new file mode 100644 index 0000000..f54ccd0 --- /dev/null +++ b/src/cleanup/cleanup_milter.in14e @@ -0,0 +1,7 @@ +#verbose on +open test-queue-file14e.tmp + +header_checks regexp:cleanup_milter.reg14e +add_header X-SPAM-FLAG YES + +close diff --git a/src/cleanup/cleanup_milter.in14f b/src/cleanup/cleanup_milter.in14f new file mode 100644 index 0000000..68124a7 --- /dev/null +++ b/src/cleanup/cleanup_milter.in14f @@ -0,0 +1,7 @@ +#verbose on +open test-queue-file14f.tmp + +header_checks regexp:cleanup_milter.reg14f +ins_header 2 X-SPAM-FLAG YES + +close diff --git a/src/cleanup/cleanup_milter.in14g b/src/cleanup/cleanup_milter.in14g new file mode 100644 index 0000000..ebd866f --- /dev/null +++ b/src/cleanup/cleanup_milter.in14g @@ -0,0 +1,7 @@ +#verbose on +open test-queue-file14g.tmp + +header_checks regexp:cleanup_milter.reg14g +upd_header 1 X-SPAM-FLAG YES + +close diff --git a/src/cleanup/cleanup_milter.in15a b/src/cleanup/cleanup_milter.in15a new file mode 100644 index 0000000..8c2be9e --- /dev/null +++ b/src/cleanup/cleanup_milter.in15a @@ -0,0 +1,7 @@ +#verbose on +open test-queue-file15a.tmp + +header_checks regexp:cleanup_milter.reg15a +add_header X-SPAM-FLAG YES + +close diff --git a/src/cleanup/cleanup_milter.in15b b/src/cleanup/cleanup_milter.in15b new file mode 100644 index 0000000..fb209ad --- /dev/null +++ b/src/cleanup/cleanup_milter.in15b @@ -0,0 +1,7 @@ +#verbose on +open test-queue-file15b.tmp + +header_checks regexp:cleanup_milter.reg15b +add_header X-SPAM-FLAG YES + +close diff --git a/src/cleanup/cleanup_milter.in15c b/src/cleanup/cleanup_milter.in15c new file mode 100644 index 0000000..3b3ef36 --- /dev/null +++ b/src/cleanup/cleanup_milter.in15c @@ -0,0 +1,7 @@ +#verbose on +open test-queue-file15c.tmp + +header_checks regexp:cleanup_milter.reg15c +add_header X-SPAM-FLAG YES + +close diff --git a/src/cleanup/cleanup_milter.in15d b/src/cleanup/cleanup_milter.in15d new file mode 100644 index 0000000..a00b143 --- /dev/null +++ b/src/cleanup/cleanup_milter.in15d @@ -0,0 +1,7 @@ +#verbose on +open test-queue-file15d.tmp + +header_checks regexp:cleanup_milter.reg15d +add_header X-SPAM-FLAG YES + +close diff --git a/src/cleanup/cleanup_milter.in15e b/src/cleanup/cleanup_milter.in15e new file mode 100644 index 0000000..1c26f59 --- /dev/null +++ b/src/cleanup/cleanup_milter.in15e @@ -0,0 +1,7 @@ +#verbose on +open test-queue-file15e.tmp + +header_checks regexp:cleanup_milter.reg15e +add_header X-SPAM-FLAG YES + +close diff --git a/src/cleanup/cleanup_milter.in15f b/src/cleanup/cleanup_milter.in15f new file mode 100644 index 0000000..8dc6a26 --- /dev/null +++ b/src/cleanup/cleanup_milter.in15f @@ -0,0 +1,7 @@ +#verbose on +open test-queue-file15f.tmp + +header_checks regexp:cleanup_milter.reg15f +ins_header 2 X-SPAM-FLAG YES + +close diff --git a/src/cleanup/cleanup_milter.in15g b/src/cleanup/cleanup_milter.in15g new file mode 100644 index 0000000..0e90d9f --- /dev/null +++ b/src/cleanup/cleanup_milter.in15g @@ -0,0 +1,7 @@ +#verbose on +open test-queue-file15g.tmp + +header_checks regexp:cleanup_milter.reg15g +ins_header 2 X-SPAM-FLAG YES + +close diff --git a/src/cleanup/cleanup_milter.in15h b/src/cleanup/cleanup_milter.in15h new file mode 100644 index 0000000..3538f79 --- /dev/null +++ b/src/cleanup/cleanup_milter.in15h @@ -0,0 +1,11 @@ +#verbose on +open test-queue-file15h.tmp + +# Test the CLEANUP_FILTER_FLAG_ALL feature. The first header with +# YES clears the flag, and the second add_header is ignored. + +header_checks regexp:cleanup_milter.reg15h +add_header X-SPAM-FLAG YES +add_header X-SPAM-FLAG NO + +close diff --git a/src/cleanup/cleanup_milter.in15i b/src/cleanup/cleanup_milter.in15i new file mode 100644 index 0000000..454036a --- /dev/null +++ b/src/cleanup/cleanup_milter.in15i @@ -0,0 +1,11 @@ +#verbose on +open test-queue-file15i.tmp + +# Test the CLEANUP_STAT_CONT flag. The first header triggers FILTER, +# but the second header triggers REJECT, so the filter is not saved. + +header_checks regexp:cleanup_milter.reg15i +add_header X-SPAM-FLAG NO +add_header X-SPAM-FLAG YES + +close diff --git a/src/cleanup/cleanup_milter.in16a b/src/cleanup/cleanup_milter.in16a new file mode 100644 index 0000000..3b7da44 --- /dev/null +++ b/src/cleanup/cleanup_milter.in16a @@ -0,0 +1,10 @@ +#verbose on +open test-queue-file16a.tmp + +# Test the BCC action in milter_header_checks. + +header_checks regexp:cleanup_milter.reg16a +add_header X-SPAM-FLAG NO +add_header X-SPAM-FLAG YES + +close diff --git a/src/cleanup/cleanup_milter.in16b b/src/cleanup/cleanup_milter.in16b new file mode 100644 index 0000000..57f6e24 --- /dev/null +++ b/src/cleanup/cleanup_milter.in16b @@ -0,0 +1,12 @@ +#verbose on +open test-queue-file16b.tmp + +# Test the add_rcpt_par action + +add_rcpt_par foo@example.com notify=never +add_rcpt_par foo@example.com notify=never +add_rcpt bar@example.com +add_rcpt_par bar@example.com orcpt=rfc822;orig-bar@example.com +add_rcpt_par bar@example.com notify=delay + +close diff --git a/src/cleanup/cleanup_milter.in2 b/src/cleanup/cleanup_milter.in2 new file mode 100644 index 0000000..c459a9f --- /dev/null +++ b/src/cleanup/cleanup_milter.in2 @@ -0,0 +1,28 @@ +#verbose on +open test-queue-file2.tmp + +# Update a short Subject: header that immediately precedes the "append +# header" pointer record. The new Subject: header value is stored +# at the end of the heap, followed by the saved "append header" +# pointer record value. +# +# - Postfix must not consider the "append header" pointer record as +# if it were part of the short Subject: header. Instead, the record +# must be saved to the heap, right after the new Subject: header +# value. +# +# - Postfix must update its idea of the current "append header" +# pointer record location. +# +# - While saving the "append header" pointer record value, Postfix +# must replace a "0" append header" pointer record value by the +# actual location of the message body content. + +upd_header 1 Subject hey! +upd_header 1 Subject hey! +upd_header 1 Subject hey! +add_header foo foobar +upd_header 1 foo foobar +upd_header 1 foo foobar +upd_header 1 foo foobar +close diff --git a/src/cleanup/cleanup_milter.in3 b/src/cleanup/cleanup_milter.in3 new file mode 100644 index 0000000..8a9b412 --- /dev/null +++ b/src/cleanup/cleanup_milter.in3 @@ -0,0 +1,60 @@ +#verbose on +open test-queue-file3.tmp + +# Another torture test for mail with a short last message header. +# This complements test #2 with the same message where we update the +# short Subject header, but none of the other headers. Like test #1, +# this also tests possible interactions with envelope recipient +# updates, which share the same heap with message header updates. + +# Add a recipient and update all headers in reverse order. + +add_rcpt me@porcupine.org +upd_header 1 Subject hey! +upd_header 1 Date Tue, 25 Jul 2006 15:27:19 -0400 (EDT) +upd_header 1 Message-Id <20060725192735.5EC2D29013F@hades.porcupine.org> +upd_header 1 To you@porcupine.org +upd_header 1 From me@porcupine.org + +# Delete the recipient added above, and update headers in reverse +# order, twice. This tests repeated updates of short headers, but +# doesn't test much for the longer ones. + +del_rcpt me@porcupine.org +upd_header 1 Subject hey! +upd_header 1 Subject hey! +upd_header 1 Date Tue, 25 Jul 2006 15:27:19 -0400 (EDT) +upd_header 1 Date Tue, 25 Jul 2006 15:27:19 -0400 (EDT) +upd_header 1 Message-Id <20060725192735.5EC2D29013F@hades.porcupine.org> +upd_header 1 Message-Id <20060725192735.5EC2D29013F@hades.porcupine.org> +upd_header 1 To you@porcupine.org +upd_header 1 To you@porcupine.org +upd_header 1 From me@porcupine.org +upd_header 1 From me@porcupine.org + +# Add a new recipient, using a different address than above, so that +# the duplicate filter won't suppress it. Update the headers in the +# normal order, in case it makes a difference. + +add_rcpt em@porcupine.org +upd_header 1 From me@porcupine.org +upd_header 1 To you@porcupine.org +upd_header 1 Message-Id <20060725192735.5EC2D29013F@hades.porcupine.org> +upd_header 1 Date Tue, 25 Jul 2006 15:27:19 -0400 (EDT) +upd_header 1 Subject hey! + +# Delete the recipient and update the headers again. + +del_rcpt em@porcupine.org +upd_header 1 From me@porcupine.org +upd_header 1 From me@porcupine.org +upd_header 1 To you@porcupine.org +upd_header 1 To you@porcupine.org +upd_header 1 Message-Id <20060725192735.5EC2D29013F@hades.porcupine.org> +upd_header 1 Message-Id <20060725192735.5EC2D29013F@hades.porcupine.org> +upd_header 1 Date Tue, 25 Jul 2006 15:27:19 -0400 (EDT) +upd_header 1 Date Tue, 25 Jul 2006 15:27:19 -0400 (EDT) +upd_header 1 Subject hey! +upd_header 1 Subject hey! + +close diff --git a/src/cleanup/cleanup_milter.in4a b/src/cleanup/cleanup_milter.in4a new file mode 100644 index 0000000..ce07907 --- /dev/null +++ b/src/cleanup/cleanup_milter.in4a @@ -0,0 +1,9 @@ +#verbose on +open test-queue-file4.tmp +add_rcpt 01 +add_rcpt 02 +add_rcpt 03 +del_rcpt 03 +del_rcpt 02 +del_rcpt 01 +close diff --git a/src/cleanup/cleanup_milter.in4b b/src/cleanup/cleanup_milter.in4b new file mode 100644 index 0000000..3e6202d --- /dev/null +++ b/src/cleanup/cleanup_milter.in4b @@ -0,0 +1,9 @@ +#verbose on +open test-queue-file4.tmp +add_rcpt 01 +add_rcpt 02 +add_rcpt 03 +del_rcpt 01 +del_rcpt 02 +del_rcpt 03 +close diff --git a/src/cleanup/cleanup_milter.in4c b/src/cleanup/cleanup_milter.in4c new file mode 100644 index 0000000..25dc6d6 --- /dev/null +++ b/src/cleanup/cleanup_milter.in4c @@ -0,0 +1,12 @@ +#verbose on +open test-queue-file4.tmp +add_rcpt 01 +del_rcpt 01 +del_rcpt 03 +add_rcpt 02 +del_rcpt 02 +del_rcpt 01 +add_rcpt 03 +del_rcpt 03 +del_rcpt 02 +close diff --git a/src/cleanup/cleanup_milter.in5 b/src/cleanup/cleanup_milter.in5 new file mode 100644 index 0000000..393e300 --- /dev/null +++ b/src/cleanup/cleanup_milter.in5 @@ -0,0 +1,19 @@ +open test-queue-file5.tmp + +# Test with a series of multiple short headers. + +# Update short multi-line X header in the middle of other headers, +# so that the next header gets copied right after the new X header. +# Then update the X header another time so that it separates from +# the header that follows it. + +upd_header 1 X whatevershebringswesing +upd_header 1 X whatevershebringswesing + +# Update a short Subject header that precedes the updated X header, +# and see if pointers are updated properly. + +upd_header 1 Subject hya +#upd_header 1 Subject hya + +close diff --git a/src/cleanup/cleanup_milter.in6a b/src/cleanup/cleanup_milter.in6a new file mode 100644 index 0000000..aec3d3a --- /dev/null +++ b/src/cleanup/cleanup_milter.in6a @@ -0,0 +1,5 @@ +open test-queue-file6.tmp + +ins_header 1 X-Virus-Scanned hya + +close diff --git a/src/cleanup/cleanup_milter.in6b b/src/cleanup/cleanup_milter.in6b new file mode 100644 index 0000000..832a54a --- /dev/null +++ b/src/cleanup/cleanup_milter.in6b @@ -0,0 +1,6 @@ +open test-queue-file6.tmp + +ins_header 1 X-Virus-Scanned hya +ins_header 2 Domainkey-Signature hya + +close diff --git a/src/cleanup/cleanup_milter.in6c b/src/cleanup/cleanup_milter.in6c new file mode 100644 index 0000000..3ef53ff --- /dev/null +++ b/src/cleanup/cleanup_milter.in6c @@ -0,0 +1,7 @@ +open test-queue-file6.tmp + +ins_header 1 X-Virus-Scanned hya +ins_header 2 Domainkey-Signature hya +ins_header 2 DKIM-Signature hya + +close diff --git a/src/cleanup/cleanup_milter.in7 b/src/cleanup/cleanup_milter.in7 new file mode 100644 index 0000000..13ff78c --- /dev/null +++ b/src/cleanup/cleanup_milter.in7 @@ -0,0 +1,7 @@ +open test-queue-file7.tmp + +ins_header 2 X-Virus-Scanned hya +ins_header 2 Domainkey-Signature hya +ins_header 2 DKIM-Signature hya + +close diff --git a/src/cleanup/cleanup_milter.in8 b/src/cleanup/cleanup_milter.in8 new file mode 100644 index 0000000..508b4ad --- /dev/null +++ b/src/cleanup/cleanup_milter.in8 @@ -0,0 +1,7 @@ +open test-queue-file8.tmp + +ins_header 1 inserted-at-1 hya +ins_header 2 inserted-at-2 hya +ins_header 3 inserted-at-3 hya + +close diff --git a/src/cleanup/cleanup_milter.in9 b/src/cleanup/cleanup_milter.in9 new file mode 100644 index 0000000..3bbdc3f --- /dev/null +++ b/src/cleanup/cleanup_milter.in9 @@ -0,0 +1,7 @@ +open test-queue-file9.tmp + +ins_header 1 inserted-at-1 hya +ins_header 3 inserted-at-3 hya +ins_header 5 inserted-at-5 hya + +close diff --git a/src/cleanup/cleanup_milter.ref1 b/src/cleanup/cleanup_milter.ref1 new file mode 100644 index 0000000..a529c62 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref1 @@ -0,0 +1,56 @@ +*** ENVELOPE RECORDS test-queue-file.tmp *** + 0 message_size: 441 813 3 0 441 + 81 message_arrival_time: Sat Jan 20 19:52:41 2007 + 100 create_time: Sat Jan 20 19:52:47 2007 + 124 named_attribute: rewrite_context=local + 147 sender: wietse@porcupine.org + 169 named_attribute: log_client_name=hades.porcupine.org + 206 named_attribute: log_client_address=168.100.189.10 + 241 named_attribute: log_message_origin=hades.porcupine.org[168.100.189.10] + 297 named_attribute: log_helo_name=hades.porcupine.org + 332 named_attribute: log_protocol_name=SMTP + 356 named_attribute: client_name=hades.porcupine.org + 389 named_attribute: reverse_client_name=hades.porcupine.org + 430 named_attribute: client_address=168.100.189.10 + 461 named_attribute: helo_name=hades.porcupine.org + 492 named_attribute: client_address_type=2 + 515 named_attribute: dsn_orig_rcpt=rfc822;wietse@porcupine.org + 558 original_recipient: wietse@porcupine.org + 580 recipient: wietse@porcupine.org + 602 named_attribute: dsn_orig_rcpt=rfc822;alias@hades.porcupine.org + 650 original_recipient: alias@hades.porcupine.org + 677 canceled_recipient: wietse@porcupine.org + 699 named_attribute: dsn_orig_rcpt=rfc822;alias@hades.porcupine.org + 747 original_recipient: alias@hades.porcupine.org + 774 canceled_recipient: root@porcupine.org + 794 pointer_record: 1258 + 1258 original_recipient: xxxx + 1264 recipient: xxxx + 1270 pointer_record: 1287 + 1287 original_recipient: yyyy + 1293 canceled_recipient: yyyy + 1299 pointer_record: 811 + 811 *** MESSAGE CONTENTS test-queue-file.tmp *** + 813 regular_text: Received: from hades.porcupine.org (hades.porcupine.org [168.100.189.10]) + 888 regular_text: by hades.porcupine.org (Postfix) with SMTP id 38132290405; + 949 regular_text: Sat, 20 Jan 2007 19:52:41 -0500 (EST) + 989 pointer_record: 1316 + 1316 pointer_record: 1367 + 1367 pointer_record: 1333 + 1333 pointer_record: 1460 + 1460 regular_text: X: X-replaced-header replacement header text + 1506 pointer_record: 1401 + 1401 regular_text: X2: test header value 3 + 1426 regular_text: Y: 1234567 + 1438 padding: 0 + 1443 pointer_record: 1047 + 1047 regular_text: Message-Id: <20070121005247.38132290405@hades.porcupine.org> + 1109 regular_text: Date: Sat, 20 Jan 2007 19:52:41 -0500 (EST) + 1154 regular_text: From: wietse@porcupine.org + 1182 regular_text: To: undisclosed-recipients:; + 1212 pointer_record: 0 + 1229 regular_text: + 1231 regular_text: text + 1237 pointer_record: 0 + 1254 *** HEADER EXTRACTED test-queue-file.tmp *** + 1256 *** MESSAGE FILE END test-queue-file.tmp *** diff --git a/src/cleanup/cleanup_milter.ref10a b/src/cleanup/cleanup_milter.ref10a new file mode 100644 index 0000000..3f5c1cb --- /dev/null +++ b/src/cleanup/cleanup_milter.ref10a @@ -0,0 +1,53 @@ +*** ENVELOPE RECORDS test-queue-file10.tmp *** + 0 message_size: 332 199 1 0 332 + 81 message_arrival_time: Sat Jan 20 20:53:54 2007 + 100 create_time: Sat Jan 20 20:53:59 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: me@porcupine.org + 180 pointer_record: 0 + 197 *** MESSAGE CONTENTS test-queue-file10.tmp *** + 199 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 261 regular_text: id B85F1290407; Sat, 20 Jan 2007 20:53:59 -0500 (EST) + 317 regular_text: From: me@porcupine.org + 341 regular_text: To: you@porcupine.org + 364 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 426 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 471 regular_text: Subject: hey! + 486 padding: 0 + 489 pointer_record: 506 + 506 regular_text: + 508 pointer_record: 573 + 573 regular_text: Sed ut perspiciatis unde omnis iste natus error sit voluptatem
+ 638 regular_text: accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
+ 705 regular_text: quae ab illo inventore veritatis et quasi architecto beatae vitae
+ 773 regular_text: dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit
+ 842 regular_text: aspernatur aut odit aut fugit, sed quia consequuntur magni dolores
+ 911 regular_text: eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam
+ 977 regular_text: est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci
+ 1045 regular_text: velit, sed quia non numquam eius modi tempora incidunt ut labore
+ 1112 regular_text: et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima
+ 1177 regular_text: veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam,
+ 1251 regular_text: nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure
+ 1320 regular_text: reprehenderit qui in ea voluptate velit esse quam nihil molestiae
+ 1388 regular_text: consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla
+ 1455 regular_text: pariatur?
+ 1467 regular_text:
+ 1470 regular_text: At vero eos et accusamus et iusto odio dignissimos ducimus qui
+ 1535 regular_text: blanditiis praesentium voluptatum deleniti atque corrupti quos
+ 1600 regular_text: dolores et quas molestias excepturi sint occaecati cupiditate non
+ 1668 regular_text: provident, similique sunt in culpa qui officia deserunt mollitia
+ 1735 regular_text: animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis
+ 1806 regular_text: est et expedita distinctio. Nam libero tempore, cum soluta nobis
+ 1873 regular_text: est eligendi optio cumque nihil impedit quo minus id quod maxime
+ 1940 regular_text: placeat facere possimus, omnis voluptas assumenda est, omnis dolor
+ 2009 regular_text: repellendus. Temporibus autem quibusdam et aut officiis debitis aut
+ 2079 regular_text: rerum necessitatibus saepe eveniet ut et voluptates repudiandae
+ 2145 regular_text: sint et molestiae non recusandae. Itaque earum rerum hic tenetur a
+ 2214 regular_text: sapiente delectus, ut aut reiciendis voluptatibus maiores alias
+ 2280 regular_text: consequatur aut perferendis doloribus asperiores repellat.
+ 2341 pointer_record: 531 + 531 *** HEADER EXTRACTED test-queue-file10.tmp *** + 533 original_recipient: you@porcupine.org + 552 recipient: you@porcupine.org + 571 *** MESSAGE FILE END test-queue-file10.tmp *** diff --git a/src/cleanup/cleanup_milter.ref10b b/src/cleanup/cleanup_milter.ref10b new file mode 100644 index 0000000..3f5c1cb --- /dev/null +++ b/src/cleanup/cleanup_milter.ref10b @@ -0,0 +1,53 @@ +*** ENVELOPE RECORDS test-queue-file10.tmp *** + 0 message_size: 332 199 1 0 332 + 81 message_arrival_time: Sat Jan 20 20:53:54 2007 + 100 create_time: Sat Jan 20 20:53:59 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: me@porcupine.org + 180 pointer_record: 0 + 197 *** MESSAGE CONTENTS test-queue-file10.tmp *** + 199 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 261 regular_text: id B85F1290407; Sat, 20 Jan 2007 20:53:59 -0500 (EST) + 317 regular_text: From: me@porcupine.org + 341 regular_text: To: you@porcupine.org + 364 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 426 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 471 regular_text: Subject: hey! + 486 padding: 0 + 489 pointer_record: 506 + 506 regular_text: + 508 pointer_record: 573 + 573 regular_text: Sed ut perspiciatis unde omnis iste natus error sit voluptatem
+ 638 regular_text: accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
+ 705 regular_text: quae ab illo inventore veritatis et quasi architecto beatae vitae
+ 773 regular_text: dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit
+ 842 regular_text: aspernatur aut odit aut fugit, sed quia consequuntur magni dolores
+ 911 regular_text: eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam
+ 977 regular_text: est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci
+ 1045 regular_text: velit, sed quia non numquam eius modi tempora incidunt ut labore
+ 1112 regular_text: et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima
+ 1177 regular_text: veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam,
+ 1251 regular_text: nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure
+ 1320 regular_text: reprehenderit qui in ea voluptate velit esse quam nihil molestiae
+ 1388 regular_text: consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla
+ 1455 regular_text: pariatur?
+ 1467 regular_text:
+ 1470 regular_text: At vero eos et accusamus et iusto odio dignissimos ducimus qui
+ 1535 regular_text: blanditiis praesentium voluptatum deleniti atque corrupti quos
+ 1600 regular_text: dolores et quas molestias excepturi sint occaecati cupiditate non
+ 1668 regular_text: provident, similique sunt in culpa qui officia deserunt mollitia
+ 1735 regular_text: animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis
+ 1806 regular_text: est et expedita distinctio. Nam libero tempore, cum soluta nobis
+ 1873 regular_text: est eligendi optio cumque nihil impedit quo minus id quod maxime
+ 1940 regular_text: placeat facere possimus, omnis voluptas assumenda est, omnis dolor
+ 2009 regular_text: repellendus. Temporibus autem quibusdam et aut officiis debitis aut
+ 2079 regular_text: rerum necessitatibus saepe eveniet ut et voluptates repudiandae
+ 2145 regular_text: sint et molestiae non recusandae. Itaque earum rerum hic tenetur a
+ 2214 regular_text: sapiente delectus, ut aut reiciendis voluptatibus maiores alias
+ 2280 regular_text: consequatur aut perferendis doloribus asperiores repellat.
+ 2341 pointer_record: 531 + 531 *** HEADER EXTRACTED test-queue-file10.tmp *** + 533 original_recipient: you@porcupine.org + 552 recipient: you@porcupine.org + 571 *** MESSAGE FILE END test-queue-file10.tmp *** diff --git a/src/cleanup/cleanup_milter.ref10c b/src/cleanup/cleanup_milter.ref10c new file mode 100644 index 0000000..d920c6b --- /dev/null +++ b/src/cleanup/cleanup_milter.ref10c @@ -0,0 +1,83 @@ +*** ENVELOPE RECORDS test-queue-file10.tmp *** + 0 message_size: 332 199 1 0 332 + 81 message_arrival_time: Sat Jan 20 20:53:54 2007 + 100 create_time: Sat Jan 20 20:53:59 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: me@porcupine.org + 180 pointer_record: 0 + 197 *** MESSAGE CONTENTS test-queue-file10.tmp *** + 199 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 261 regular_text: id B85F1290407; Sat, 20 Jan 2007 20:53:59 -0500 (EST) + 317 regular_text: From: me@porcupine.org + 341 regular_text: To: you@porcupine.org + 364 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 426 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 471 regular_text: Subject: hey! + 486 padding: 0 + 489 pointer_record: 506 + 506 regular_text: + 508 pointer_record: 573 + 573 regular_text: Sed ut perspiciatis unde omnis iste natus error sit voluptatem
+ 638 regular_text: accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
+ 705 regular_text: quae ab illo inventore veritatis et quasi architecto beatae vitae
+ 773 regular_text: dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit
+ 842 regular_text: aspernatur aut odit aut fugit, sed quia consequuntur magni dolores
+ 911 regular_text: eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam
+ 977 regular_text: est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci
+ 1045 regular_text: velit, sed quia non numquam eius modi tempora incidunt ut labore
+ 1112 regular_text: et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima
+ 1177 regular_text: veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam,
+ 1251 regular_text: nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure
+ 1320 regular_text: reprehenderit qui in ea voluptate velit esse quam nihil molestiae
+ 1388 regular_text: consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla
+ 1455 regular_text: pariatur?
+ 1467 regular_text:
+ 1470 regular_text: At vero eos et accusamus et iusto odio dignissimos ducimus qui
+ 1535 regular_text: blanditiis praesentium voluptatum deleniti atque corrupti quos
+ 1600 regular_text: dolores et quas molestias excepturi sint occaecati cupiditate non
+ 1668 regular_text: provident, similique sunt in culpa qui officia deserunt mollitia
+ 1735 regular_text: animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis
+ 1806 regular_text: est et expedita distinctio. Nam libero tempore, cum soluta nobis
+ 1873 regular_text: est eligendi optio cumque nihil impedit quo minus id quod maxime
+ 1940 regular_text: placeat facere possimus, omnis voluptas assumenda est, omnis dolor
+ 2009 regular_text: repellendus. Temporibus autem quibusdam et aut officiis debitis aut
+ 2079 regular_text: rerum necessitatibus saepe eveniet ut et voluptates repudiandae
+ 2145 regular_text: sint et molestiae non recusandae. Itaque earum rerum hic tenetur a
+ 2214 regular_text: sapiente delectus, ut aut reiciendis voluptatibus maiores alias
+ 2280 regular_text: consequatur aut perferendis doloribus asperiores repellat.
+ 2341 pointer_record: 2358 + 2358 regular_text:
+ 2361 regular_text: Sed ut perspiciatis unde omnis iste natus error sit voluptatem
+ 2426 regular_text: accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
+ 2493 regular_text: quae ab illo inventore veritatis et quasi architecto beatae vitae
+ 2561 regular_text: dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit
+ 2630 regular_text: aspernatur aut odit aut fugit, sed quia consequuntur magni dolores
+ 2699 regular_text: eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam
+ 2765 regular_text: est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci
+ 2833 regular_text: velit, sed quia non numquam eius modi tempora incidunt ut labore
+ 2900 regular_text: et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima
+ 2965 regular_text: veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam,
+ 3039 regular_text: nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure
+ 3108 regular_text: reprehenderit qui in ea voluptate velit esse quam nihil molestiae
+ 3176 regular_text: consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla
+ 3243 regular_text: pariatur?
+ 3255 regular_text:
+ 3258 regular_text: At vero eos et accusamus et iusto odio dignissimos ducimus qui
+ 3323 regular_text: blanditiis praesentium voluptatum deleniti atque corrupti quos
+ 3388 regular_text: dolores et quas molestias excepturi sint occaecati cupiditate non
+ 3456 regular_text: provident, similique sunt in culpa qui officia deserunt mollitia
+ 3523 regular_text: animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis
+ 3594 regular_text: est et expedita distinctio. Nam libero tempore, cum soluta nobis
+ 3661 regular_text: est eligendi optio cumque nihil impedit quo minus id quod maxime
+ 3728 regular_text: placeat facere possimus, omnis voluptas assumenda est, omnis dolor
+ 3797 regular_text: repellendus. Temporibus autem quibusdam et aut officiis debitis aut
+ 3867 regular_text: rerum necessitatibus saepe eveniet ut et voluptates repudiandae
+ 3933 regular_text: sint et molestiae non recusandae. Itaque earum rerum hic tenetur a
+ 4002 regular_text: sapiente delectus, ut aut reiciendis voluptatibus maiores alias
+ 4068 regular_text: consequatur aut perferendis doloribus asperiores repellat.
+ 4129 pointer_record: 531 + 531 *** HEADER EXTRACTED test-queue-file10.tmp *** + 533 original_recipient: you@porcupine.org + 552 recipient: you@porcupine.org + 571 *** MESSAGE FILE END test-queue-file10.tmp *** diff --git a/src/cleanup/cleanup_milter.ref10d b/src/cleanup/cleanup_milter.ref10d new file mode 100644 index 0000000..3f5c1cb --- /dev/null +++ b/src/cleanup/cleanup_milter.ref10d @@ -0,0 +1,53 @@ +*** ENVELOPE RECORDS test-queue-file10.tmp *** + 0 message_size: 332 199 1 0 332 + 81 message_arrival_time: Sat Jan 20 20:53:54 2007 + 100 create_time: Sat Jan 20 20:53:59 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: me@porcupine.org + 180 pointer_record: 0 + 197 *** MESSAGE CONTENTS test-queue-file10.tmp *** + 199 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 261 regular_text: id B85F1290407; Sat, 20 Jan 2007 20:53:59 -0500 (EST) + 317 regular_text: From: me@porcupine.org + 341 regular_text: To: you@porcupine.org + 364 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 426 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 471 regular_text: Subject: hey! + 486 padding: 0 + 489 pointer_record: 506 + 506 regular_text: + 508 pointer_record: 573 + 573 regular_text: Sed ut perspiciatis unde omnis iste natus error sit voluptatem
+ 638 regular_text: accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
+ 705 regular_text: quae ab illo inventore veritatis et quasi architecto beatae vitae
+ 773 regular_text: dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit
+ 842 regular_text: aspernatur aut odit aut fugit, sed quia consequuntur magni dolores
+ 911 regular_text: eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam
+ 977 regular_text: est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci
+ 1045 regular_text: velit, sed quia non numquam eius modi tempora incidunt ut labore
+ 1112 regular_text: et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima
+ 1177 regular_text: veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam,
+ 1251 regular_text: nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure
+ 1320 regular_text: reprehenderit qui in ea voluptate velit esse quam nihil molestiae
+ 1388 regular_text: consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla
+ 1455 regular_text: pariatur?
+ 1467 regular_text:
+ 1470 regular_text: At vero eos et accusamus et iusto odio dignissimos ducimus qui
+ 1535 regular_text: blanditiis praesentium voluptatum deleniti atque corrupti quos
+ 1600 regular_text: dolores et quas molestias excepturi sint occaecati cupiditate non
+ 1668 regular_text: provident, similique sunt in culpa qui officia deserunt mollitia
+ 1735 regular_text: animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis
+ 1806 regular_text: est et expedita distinctio. Nam libero tempore, cum soluta nobis
+ 1873 regular_text: est eligendi optio cumque nihil impedit quo minus id quod maxime
+ 1940 regular_text: placeat facere possimus, omnis voluptas assumenda est, omnis dolor
+ 2009 regular_text: repellendus. Temporibus autem quibusdam et aut officiis debitis aut
+ 2079 regular_text: rerum necessitatibus saepe eveniet ut et voluptates repudiandae
+ 2145 regular_text: sint et molestiae non recusandae. Itaque earum rerum hic tenetur a
+ 2214 regular_text: sapiente delectus, ut aut reiciendis voluptatibus maiores alias
+ 2280 regular_text: consequatur aut perferendis doloribus asperiores repellat.
+ 2341 pointer_record: 531 + 531 *** HEADER EXTRACTED test-queue-file10.tmp *** + 533 original_recipient: you@porcupine.org + 552 recipient: you@porcupine.org + 571 *** MESSAGE FILE END test-queue-file10.tmp *** diff --git a/src/cleanup/cleanup_milter.ref10e b/src/cleanup/cleanup_milter.ref10e new file mode 100644 index 0000000..490c622 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref10e @@ -0,0 +1,89 @@ +*** ENVELOPE RECORDS test-queue-file10.tmp *** + 0 message_size: 332 199 1 0 332 + 81 message_arrival_time: Sat Jan 20 20:53:54 2007 + 100 create_time: Sat Jan 20 20:53:59 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: me@porcupine.org + 180 pointer_record: 0 + 197 *** MESSAGE CONTENTS test-queue-file10.tmp *** + 199 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 261 regular_text: id B85F1290407; Sat, 20 Jan 2007 20:53:59 -0500 (EST) + 317 regular_text: From: me@porcupine.org + 341 regular_text: To: you@porcupine.org + 364 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 426 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 471 regular_text: Subject: hey! + 486 padding: 0 + 489 pointer_record: 573 + 573 regular_text: foo1: foobar + 587 padding: 0 + 590 pointer_record: 2392 + 2392 regular_text: foo2: foobar + 2406 padding: 0 + 2409 pointer_record: 506 + 506 regular_text: + 508 pointer_record: 607 + 607 regular_text: Sed ut perspiciatis unde omnis iste natus error sit voluptatem
+ 672 regular_text: accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
+ 739 regular_text: quae ab illo inventore veritatis et quasi architecto beatae vitae
+ 807 regular_text: dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit
+ 876 regular_text: aspernatur aut odit aut fugit, sed quia consequuntur magni dolores
+ 945 regular_text: eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam
+ 1011 regular_text: est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci
+ 1079 regular_text: velit, sed quia non numquam eius modi tempora incidunt ut labore
+ 1146 regular_text: et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima
+ 1211 regular_text: veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam,
+ 1285 regular_text: nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure
+ 1354 regular_text: reprehenderit qui in ea voluptate velit esse quam nihil molestiae
+ 1422 regular_text: consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla
+ 1489 regular_text: pariatur?
+ 1501 regular_text:
+ 1504 regular_text: At vero eos et accusamus et iusto odio dignissimos ducimus qui
+ 1569 regular_text: blanditiis praesentium voluptatum deleniti atque corrupti quos
+ 1634 regular_text: dolores et quas molestias excepturi sint occaecati cupiditate non
+ 1702 regular_text: provident, similique sunt in culpa qui officia deserunt mollitia
+ 1769 regular_text: animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis
+ 1840 regular_text: est et expedita distinctio. Nam libero tempore, cum soluta nobis
+ 1907 regular_text: est eligendi optio cumque nihil impedit quo minus id quod maxime
+ 1974 regular_text: placeat facere possimus, omnis voluptas assumenda est, omnis dolor
+ 2043 regular_text: repellendus. Temporibus autem quibusdam et aut officiis debitis aut
+ 2113 regular_text: rerum necessitatibus saepe eveniet ut et voluptates repudiandae
+ 2179 regular_text: sint et molestiae non recusandae. Itaque earum rerum hic tenetur a
+ 2248 regular_text: sapiente delectus, ut aut reiciendis voluptatibus maiores alias
+ 2314 regular_text: consequatur aut perferendis doloribus asperiores repellat.
+ 2375 pointer_record: 2426 + 2426 regular_text:
+ 2429 regular_text: Sed ut perspiciatis unde omnis iste natus error sit voluptatem
+ 2494 regular_text: accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
+ 2561 regular_text: quae ab illo inventore veritatis et quasi architecto beatae vitae
+ 2629 regular_text: dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit
+ 2698 regular_text: aspernatur aut odit aut fugit, sed quia consequuntur magni dolores
+ 2767 regular_text: eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam
+ 2833 regular_text: est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci
+ 2901 regular_text: velit, sed quia non numquam eius modi tempora incidunt ut labore
+ 2968 regular_text: et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima
+ 3033 regular_text: veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam,
+ 3107 regular_text: nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure
+ 3176 regular_text: reprehenderit qui in ea voluptate velit esse quam nihil molestiae
+ 3244 regular_text: consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla
+ 3311 regular_text: pariatur?
+ 3323 regular_text:
+ 3326 regular_text: At vero eos et accusamus et iusto odio dignissimos ducimus qui
+ 3391 regular_text: blanditiis praesentium voluptatum deleniti atque corrupti quos
+ 3456 regular_text: dolores et quas molestias excepturi sint occaecati cupiditate non
+ 3524 regular_text: provident, similique sunt in culpa qui officia deserunt mollitia
+ 3591 regular_text: animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis
+ 3662 regular_text: est et expedita distinctio. Nam libero tempore, cum soluta nobis
+ 3729 regular_text: est eligendi optio cumque nihil impedit quo minus id quod maxime
+ 3796 regular_text: placeat facere possimus, omnis voluptas assumenda est, omnis dolor
+ 3865 regular_text: repellendus. Temporibus autem quibusdam et aut officiis debitis aut
+ 3935 regular_text: rerum necessitatibus saepe eveniet ut et voluptates repudiandae
+ 4001 regular_text: sint et molestiae non recusandae. Itaque earum rerum hic tenetur a
+ 4070 regular_text: sapiente delectus, ut aut reiciendis voluptatibus maiores alias
+ 4136 regular_text: consequatur aut perferendis doloribus asperiores repellat.
+ 4197 pointer_record: 531 + 531 *** HEADER EXTRACTED test-queue-file10.tmp *** + 533 original_recipient: you@porcupine.org + 552 recipient: you@porcupine.org + 571 *** MESSAGE FILE END test-queue-file10.tmp *** diff --git a/src/cleanup/cleanup_milter.ref11 b/src/cleanup/cleanup_milter.ref11 new file mode 100644 index 0000000..f8be5d4 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref11 @@ -0,0 +1,66 @@ +*** ENVELOPE RECORDS test-queue-file11.tmp *** + 0 message_size: 366 605 1 0 366 + 81 message_arrival_time: Mon Apr 27 20:41:30 2009 + 100 create_time: Mon Apr 27 20:41:41 2009 + 124 named_attribute: rewrite_context=local + 147 sender: + 149 pointer_record: 0 + 164 named_attribute: log_client_name=localhost + 191 named_attribute: log_client_address=127.0.0.1 + 221 named_attribute: log_client_port=51286 + 244 named_attribute: log_message_origin=localhost[127.0.0.1] + 285 named_attribute: log_helo_name=localhost + 310 named_attribute: log_protocol_name=SMTP + 334 named_attribute: client_name=localhost + 357 named_attribute: reverse_client_name=localhost + 388 named_attribute: client_address=127.0.0.1 + 414 named_attribute: client_port=51286 + 433 named_attribute: helo_name=localhost + 454 named_attribute: protocol_name=SMTP + 474 named_attribute: client_address_type=2 + 497 named_attribute: dsn_orig_rcpt=rfc822;wietse@localhost + 536 original_recipient: wietse@localhost + 554 recipient: wietse@localhost.porcupine.org + 586 pointer_record: 0 + 603 *** MESSAGE CONTENTS test-queue-file11.tmp *** + 605 regular_text: Received: from localhost (localhost [127.0.0.1]) + 655 regular_text: by hades.porcupine.org (Postfix) with SMTP id 382B12B3292 + 715 regular_text: for <wietse@localhost>; Mon, 27 Apr 2009 20:41:30 -0400 (EDT) + 779 regular_text: Message-Id: <20090428004141.382B12B3292@hades.porcupine.org> + 841 regular_text: Date: Mon, 27 Apr 2009 20:41:30 -0400 (EDT) + 886 regular_text: From: MAILER-DAEMON + 907 regular_text: To: undisclosed-recipients:; + 937 pointer_record: 954 + 954 pointer_record: 975 + 975 regular_text: + 977 regular_text: Sed ut perspiciatis unde omnis iste natus error sit voluptatem
+ 1042 regular_text: accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
+ 1109 regular_text: quae ab illo inventore veritatis et quasi architecto beatae vitae
+ 1177 regular_text: dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit
+ 1246 regular_text: aspernatur aut odit aut fugit, sed quia consequuntur magni dolores
+ 1315 regular_text: eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam
+ 1381 regular_text: est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci
+ 1449 regular_text: velit, sed quia non numquam eius modi tempora incidunt ut labore
+ 1516 regular_text: et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima
+ 1581 regular_text: veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam,
+ 1655 regular_text: nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure
+ 1724 regular_text: reprehenderit qui in ea voluptate velit esse quam nihil molestiae
+ 1792 regular_text: consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla
+ 1859 regular_text: pariatur?
+ 1871 regular_text:
+ 1874 regular_text: At vero eos et accusamus et iusto odio dignissimos ducimus qui
+ 1939 regular_text: blanditiis praesentium voluptatum deleniti atque corrupti quos
+ 2004 regular_text: dolores et quas molestias excepturi sint occaecati cupiditate non
+ 2072 regular_text: provident, similique sunt in culpa qui officia deserunt mollitia
+ 2139 regular_text: animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis
+ 2210 regular_text: est et expedita distinctio. Nam libero tempore, cum soluta nobis
+ 2277 regular_text: est eligendi optio cumque nihil impedit quo minus id quod maxime
+ 2344 regular_text: placeat facere possimus, omnis voluptas assumenda est, omnis dolor
+ 2413 regular_text: repellendus. Temporibus autem quibusdam et aut officiis debitis aut
+ 2483 regular_text: rerum necessitatibus saepe eveniet ut et voluptates repudiandae
+ 2549 regular_text: sint et molestiae non recusandae. Itaque earum rerum hic tenetur a
+ 2618 regular_text: sapiente delectus, ut aut reiciendis voluptatibus maiores alias
+ 2684 regular_text: consequatur aut perferendis doloribus asperiores repellat.
+ 2745 pointer_record: 971 + 971 *** HEADER EXTRACTED test-queue-file11.tmp *** + 973 *** MESSAGE FILE END test-queue-file11.tmp *** diff --git a/src/cleanup/cleanup_milter.ref12 b/src/cleanup/cleanup_milter.ref12 new file mode 100644 index 0000000..d5d0f2c --- /dev/null +++ b/src/cleanup/cleanup_milter.ref12 @@ -0,0 +1,31 @@ +*** ENVELOPE RECORDS test-queue-file12.tmp *** + 0 message_size: 332 182 1 0 332 + 81 message_arrival_time: Sun Jan 21 13:32:59 2007 + 100 create_time: Sun Jan 21 13:33:08 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: me@porcupine.org + 180 *** MESSAGE CONTENTS test-queue-file12.tmp *** + 182 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 244 regular_text: id DE040290405; Sun, 21 Jan 2007 13:33:08 -0500 (EST) + 300 regular_text: From: me@porcupine.org + 324 regular_text: To: you@porcupine.org + 347 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 409 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 454 regular_text: Subject: hey! + 469 padding: 0 + 472 pointer_record: 0 + 489 regular_text: + 491 regular_text: text + 497 pointer_record: 0 + 514 *** HEADER EXTRACTED test-queue-file12.tmp *** + 516 original_recipient: you@porcupine.org + 535 recipient: you@porcupine.org + 554 pointer_record: 573 + 573 original_recipient: me@porcupine.org + 591 canceled_recipient: me@porcupine.org + 609 pointer_record: 626 + 626 original_recipient: em@porcupine.org + 644 canceled_recipient: em@porcupine.org + 662 pointer_record: 571 + 571 *** MESSAGE FILE END test-queue-file12.tmp *** diff --git a/src/cleanup/cleanup_milter.ref13a b/src/cleanup/cleanup_milter.ref13a new file mode 100644 index 0000000..3ccf028 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref13a @@ -0,0 +1,31 @@ +*** ENVELOPE RECORDS test-queue-file13a.tmp *** + 0 message_size: 332 182 1 0 332 + 81 message_arrival_time: Sun Jan 21 13:32:59 2007 + 100 create_time: Sun Jan 21 13:33:08 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: me@porcupine.org + 180 *** MESSAGE CONTENTS test-queue-file13a.tmp *** + 182 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 244 regular_text: id DE040290405; Sun, 21 Jan 2007 13:33:08 -0500 (EST) + 300 regular_text: From: me@porcupine.org + 324 regular_text: To: you@porcupine.org + 347 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 409 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 454 regular_text: Subject: hey! + 469 padding: 0 + 472 pointer_record: 0 + 489 regular_text: + 491 regular_text: text + 497 pointer_record: 0 + 514 *** HEADER EXTRACTED test-queue-file13a.tmp *** + 516 original_recipient: you@porcupine.org + 535 recipient: you@porcupine.org + 554 pointer_record: 573 + 573 original_recipient: me@porcupine.org + 591 canceled_recipient: me@porcupine.org + 609 pointer_record: 626 + 626 original_recipient: em@porcupine.org + 644 canceled_recipient: em@porcupine.org + 662 pointer_record: 571 + 571 *** MESSAGE FILE END test-queue-file13a.tmp *** diff --git a/src/cleanup/cleanup_milter.ref13b b/src/cleanup/cleanup_milter.ref13b new file mode 100644 index 0000000..bb55fb6 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref13b @@ -0,0 +1,27 @@ +*** ENVELOPE RECORDS test-queue-file13b.tmp *** + 0 message_size: 332 182 1 0 332 + 81 message_arrival_time: Sun Jan 21 13:32:59 2007 + 100 create_time: Sun Jan 21 13:33:08 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 pointer_record: 573 + 573 sender: m@porcupine.org + 590 pointer_record: 180 + 180 *** MESSAGE CONTENTS test-queue-file13b.tmp *** + 182 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 244 regular_text: id DE040290405; Sun, 21 Jan 2007 13:33:08 -0500 (EST) + 300 regular_text: From: me@porcupine.org + 324 regular_text: To: you@porcupine.org + 347 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 409 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 454 regular_text: Subject: hey! + 469 padding: 0 + 472 pointer_record: 0 + 489 regular_text: + 491 regular_text: text + 497 pointer_record: 0 + 514 *** HEADER EXTRACTED test-queue-file13b.tmp *** + 516 original_recipient: you@porcupine.org + 535 recipient: you@porcupine.org + 554 pointer_record: 0 + 571 *** MESSAGE FILE END test-queue-file13b.tmp *** diff --git a/src/cleanup/cleanup_milter.ref13c b/src/cleanup/cleanup_milter.ref13c new file mode 100644 index 0000000..3ec0531 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref13c @@ -0,0 +1,29 @@ +*** ENVELOPE RECORDS test-queue-file13c.tmp *** + 0 message_size: 332 182 1 0 332 + 81 message_arrival_time: Sun Jan 21 13:32:59 2007 + 100 create_time: Sun Jan 21 13:33:08 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 pointer_record: 573 + 573 pointer_record: 607 + 607 sender: n@porcupine.org + 624 pointer_record: 590 + 590 pointer_record: 180 + 180 *** MESSAGE CONTENTS test-queue-file13c.tmp *** + 182 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 244 regular_text: id DE040290405; Sun, 21 Jan 2007 13:33:08 -0500 (EST) + 300 regular_text: From: me@porcupine.org + 324 regular_text: To: you@porcupine.org + 347 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 409 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 454 regular_text: Subject: hey! + 469 padding: 0 + 472 pointer_record: 0 + 489 regular_text: + 491 regular_text: text + 497 pointer_record: 0 + 514 *** HEADER EXTRACTED test-queue-file13c.tmp *** + 516 original_recipient: you@porcupine.org + 535 recipient: you@porcupine.org + 554 pointer_record: 0 + 571 *** MESSAGE FILE END test-queue-file13c.tmp *** diff --git a/src/cleanup/cleanup_milter.ref13d b/src/cleanup/cleanup_milter.ref13d new file mode 100644 index 0000000..df9f1dc --- /dev/null +++ b/src/cleanup/cleanup_milter.ref13d @@ -0,0 +1,39 @@ +*** ENVELOPE RECORDS test-queue-file13d.tmp *** + 0 message_size: 366 605 1 0 366 + 81 message_arrival_time: Mon Apr 27 20:41:30 2009 + 100 create_time: Mon Apr 27 20:41:41 2009 + 124 named_attribute: rewrite_context=local + 147 pointer_record: 975 + 975 pointer_record: 1009 + 1009 sender: n@porcupine.org + 1026 pointer_record: 992 + 992 pointer_record: 164 + 164 named_attribute: log_client_name=localhost + 191 named_attribute: log_client_address=127.0.0.1 + 221 named_attribute: log_client_port=51286 + 244 named_attribute: log_message_origin=localhost[127.0.0.1] + 285 named_attribute: log_helo_name=localhost + 310 named_attribute: log_protocol_name=SMTP + 334 named_attribute: client_name=localhost + 357 named_attribute: reverse_client_name=localhost + 388 named_attribute: client_address=127.0.0.1 + 414 named_attribute: client_port=51286 + 433 named_attribute: helo_name=localhost + 454 named_attribute: protocol_name=SMTP + 474 named_attribute: client_address_type=2 + 497 named_attribute: dsn_orig_rcpt=rfc822;wietse@localhost + 536 original_recipient: wietse@localhost + 554 recipient: wietse@localhost.porcupine.org + 586 pointer_record: 0 + 603 *** MESSAGE CONTENTS test-queue-file13d.tmp *** + 605 regular_text: Received: from localhost (localhost [127.0.0.1]) + 655 regular_text: by hades.porcupine.org (Postfix) with SMTP id 382B12B3292 + 715 regular_text: for <wietse@localhost>; Mon, 27 Apr 2009 20:41:30 -0400 (EDT) + 779 regular_text: Message-Id: <20090428004141.382B12B3292@hades.porcupine.org> + 841 regular_text: Date: Mon, 27 Apr 2009 20:41:30 -0400 (EDT) + 886 regular_text: From: MAILER-DAEMON + 907 regular_text: To: undisclosed-recipients:; + 937 pointer_record: 0 + 954 pointer_record: 0 + 971 *** HEADER EXTRACTED test-queue-file13d.tmp *** + 973 *** MESSAGE FILE END test-queue-file13d.tmp *** diff --git a/src/cleanup/cleanup_milter.ref13e b/src/cleanup/cleanup_milter.ref13e new file mode 100644 index 0000000..495c7fa --- /dev/null +++ b/src/cleanup/cleanup_milter.ref13e @@ -0,0 +1,30 @@ +*** ENVELOPE RECORDS test-queue-file13e.tmp *** + 0 message_size: 332 182 1 0 332 + 81 message_arrival_time: Sun Jan 21 13:32:59 2007 + 100 create_time: Sun Jan 21 13:33:08 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 pointer_record: 573 + 573 sender: m@porcupine.org + 590 named_attribute: notify_flags=1 + 606 original_recipient: a@porcupine.org + 623 recipient: a@porcupine.org + 640 pointer_record: 180 + 180 *** MESSAGE CONTENTS test-queue-file13e.tmp *** + 182 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 244 regular_text: id DE040290405; Sun, 21 Jan 2007 13:33:08 -0500 (EST) + 300 regular_text: From: me@porcupine.org + 324 regular_text: To: you@porcupine.org + 347 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 409 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 454 regular_text: Subject: hey! + 469 padding: 0 + 472 pointer_record: 0 + 489 regular_text: + 491 regular_text: text + 497 pointer_record: 0 + 514 *** HEADER EXTRACTED test-queue-file13e.tmp *** + 516 original_recipient: you@porcupine.org + 535 recipient: you@porcupine.org + 554 pointer_record: 0 + 571 *** MESSAGE FILE END test-queue-file13e.tmp *** diff --git a/src/cleanup/cleanup_milter.ref13f b/src/cleanup/cleanup_milter.ref13f new file mode 100644 index 0000000..dcc563a --- /dev/null +++ b/src/cleanup/cleanup_milter.ref13f @@ -0,0 +1,32 @@ +*** ENVELOPE RECORDS test-queue-file13f.tmp *** + 0 message_size: 332 182 1 0 332 + 81 message_arrival_time: Sun Jan 21 13:32:59 2007 + 100 create_time: Sun Jan 21 13:33:08 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 pointer_record: 573 + 573 pointer_record: 657 + 657 sender: n@porcupine.org + 674 pointer_record: 590 + 590 named_attribute: notify_flags=1 + 606 original_recipient: a@porcupine.org + 623 recipient: a@porcupine.org + 640 pointer_record: 180 + 180 *** MESSAGE CONTENTS test-queue-file13f.tmp *** + 182 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 244 regular_text: id DE040290405; Sun, 21 Jan 2007 13:33:08 -0500 (EST) + 300 regular_text: From: me@porcupine.org + 324 regular_text: To: you@porcupine.org + 347 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 409 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 454 regular_text: Subject: hey! + 469 padding: 0 + 472 pointer_record: 0 + 489 regular_text: + 491 regular_text: text + 497 pointer_record: 0 + 514 *** HEADER EXTRACTED test-queue-file13f.tmp *** + 516 original_recipient: you@porcupine.org + 535 recipient: you@porcupine.org + 554 pointer_record: 0 + 571 *** MESSAGE FILE END test-queue-file13f.tmp *** diff --git a/src/cleanup/cleanup_milter.ref13g b/src/cleanup/cleanup_milter.ref13g new file mode 100644 index 0000000..acadf22 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref13g @@ -0,0 +1,34 @@ +*** ENVELOPE RECORDS test-queue-file13g.tmp *** + 0 message_size: 332 182 1 0 332 + 81 message_arrival_time: Sun Jan 21 13:32:59 2007 + 100 create_time: Sun Jan 21 13:33:08 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 pointer_record: 573 + 573 pointer_record: 657 + 657 pointer_record: 691 + 691 sender: o@porcupine.org + 708 pointer_record: 674 + 674 pointer_record: 590 + 590 named_attribute: notify_flags=1 + 606 original_recipient: a@porcupine.org + 623 recipient: a@porcupine.org + 640 pointer_record: 180 + 180 *** MESSAGE CONTENTS test-queue-file13g.tmp *** + 182 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 244 regular_text: id DE040290405; Sun, 21 Jan 2007 13:33:08 -0500 (EST) + 300 regular_text: From: me@porcupine.org + 324 regular_text: To: you@porcupine.org + 347 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 409 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 454 regular_text: Subject: hey! + 469 padding: 0 + 472 pointer_record: 0 + 489 regular_text: + 491 regular_text: text + 497 pointer_record: 0 + 514 *** HEADER EXTRACTED test-queue-file13g.tmp *** + 516 original_recipient: you@porcupine.org + 535 recipient: you@porcupine.org + 554 pointer_record: 0 + 571 *** MESSAGE FILE END test-queue-file13g.tmp *** diff --git a/src/cleanup/cleanup_milter.ref13h b/src/cleanup/cleanup_milter.ref13h new file mode 100644 index 0000000..c3a8fe9 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref13h @@ -0,0 +1,29 @@ +*** ENVELOPE RECORDS test-queue-file13h.tmp *** + 0 message_size: 332 182 1 0 332 + 81 message_arrival_time: Sun Jan 21 13:32:59 2007 + 100 create_time: Sun Jan 21 13:33:08 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 pointer_record: 573 + 573 named_attribute: envelope_id=env-for-m + 596 named_attribute: ret_flags=2 + 609 sender: m@porcupine.org + 626 pointer_record: 180 + 180 *** MESSAGE CONTENTS test-queue-file13h.tmp *** + 182 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 244 regular_text: id DE040290405; Sun, 21 Jan 2007 13:33:08 -0500 (EST) + 300 regular_text: From: me@porcupine.org + 324 regular_text: To: you@porcupine.org + 347 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 409 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 454 regular_text: Subject: hey! + 469 padding: 0 + 472 pointer_record: 0 + 489 regular_text: + 491 regular_text: text + 497 pointer_record: 0 + 514 *** HEADER EXTRACTED test-queue-file13h.tmp *** + 516 original_recipient: you@porcupine.org + 535 recipient: you@porcupine.org + 554 pointer_record: 0 + 571 *** MESSAGE FILE END test-queue-file13h.tmp *** diff --git a/src/cleanup/cleanup_milter.ref13i b/src/cleanup/cleanup_milter.ref13i new file mode 100644 index 0000000..006ef13 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref13i @@ -0,0 +1,33 @@ +*** ENVELOPE RECORDS test-queue-file13i.tmp *** + 0 message_size: 332 182 1 0 332 + 81 message_arrival_time: Sun Jan 21 13:32:59 2007 + 100 create_time: Sun Jan 21 13:33:08 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 pointer_record: 573 + 573 named_attribute: envelope_id=env-for-m + 596 named_attribute: ret_flags=2 + 609 pointer_record: 643 + 643 named_attribute: envelope_id=env-for-n + 666 named_attribute: ret_flags=1 + 679 sender: n@porcupine.org + 696 pointer_record: 626 + 626 pointer_record: 180 + 180 *** MESSAGE CONTENTS test-queue-file13i.tmp *** + 182 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 244 regular_text: id DE040290405; Sun, 21 Jan 2007 13:33:08 -0500 (EST) + 300 regular_text: From: me@porcupine.org + 324 regular_text: To: you@porcupine.org + 347 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 409 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 454 regular_text: Subject: hey! + 469 padding: 0 + 472 pointer_record: 0 + 489 regular_text: + 491 regular_text: text + 497 pointer_record: 0 + 514 *** HEADER EXTRACTED test-queue-file13i.tmp *** + 516 original_recipient: you@porcupine.org + 535 recipient: you@porcupine.org + 554 pointer_record: 0 + 571 *** MESSAGE FILE END test-queue-file13i.tmp *** diff --git a/src/cleanup/cleanup_milter.ref14a1 b/src/cleanup/cleanup_milter.ref14a1 new file mode 100644 index 0000000..9006f9a --- /dev/null +++ b/src/cleanup/cleanup_milter.ref14a1 @@ -0,0 +1,2 @@ +./cleanup_milter: NOQUEUE: milter-header-reject: header X-SPAM-FLAG: YES from client_name[client_addr]; from=<sender> to=<recipient>: 5.7.1 message content rejected +./cleanup_milter: errs = message content rejected diff --git a/src/cleanup/cleanup_milter.ref14a2 b/src/cleanup/cleanup_milter.ref14a2 new file mode 100644 index 0000000..cb690d8 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref14a2 @@ -0,0 +1,27 @@ +*** ENVELOPE RECORDS test-queue-file14a.tmp *** + 0 message_size: 365 256 1 0 365 + 81 message_arrival_time: Fri Jun 5 14:05:19 2009 + 100 create_time: Fri Jun 5 14:05:19 2009 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: wietse@ahost.example.com + 188 pointer_record: 0 + 203 original_recipient: wietse + 211 recipient: wietse@ahost.example.com + 237 pointer_record: 0 + 254 *** MESSAGE CONTENTS test-queue-file14a.tmp *** + 256 regular_text: Received: by ahost.example.com (Postfix, from userid 1001) + 316 regular_text: id DA4892510C1; Fri, 5 Jun 2009 14:05:19 -0400 (EDT) + 372 regular_text: To: wietse@ahost.example.com + 402 regular_text: Message-Id: <20090605180519.DA4892510C1@ahost.example.com> + 462 regular_text: Date: Fri, 5 Jun 2009 14:05:19 -0400 (EDT) + 507 regular_text: From: wietse@ahost.example.com (Wietse Venema) + 555 pointer_record: 642 + 642 regular_text: X-SPAM-FLAG: YES + 660 pointer_record: 572 + 572 regular_text: + 574 regular_text: Fri Jun 5 14:05:19 EDT 2009 + 604 pointer_record: 0 + 621 *** HEADER EXTRACTED test-queue-file14a.tmp *** + 623 pointer_record: 0 + 640 *** MESSAGE FILE END test-queue-file14a.tmp *** diff --git a/src/cleanup/cleanup_milter.ref14b1 b/src/cleanup/cleanup_milter.ref14b1 new file mode 100644 index 0000000..5608b81 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref14b1 @@ -0,0 +1,2 @@ +./cleanup_milter: NOQUEUE: milter-header-filter: header X-SPAM-FLAG: YES from client_name[client_addr]; from=<sender> to=<recipient>: transport:nexthop:port +./cleanup_milter: flags = enable_header_body_filter enable_milters diff --git a/src/cleanup/cleanup_milter.ref14b2 b/src/cleanup/cleanup_milter.ref14b2 new file mode 100644 index 0000000..209bf2b --- /dev/null +++ b/src/cleanup/cleanup_milter.ref14b2 @@ -0,0 +1,29 @@ +*** ENVELOPE RECORDS test-queue-file14b.tmp *** + 0 message_size: 365 256 1 0 365 + 81 message_arrival_time: Fri Jun 5 14:05:19 2009 + 100 create_time: Fri Jun 5 14:05:19 2009 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: wietse@ahost.example.com + 188 pointer_record: 0 + 203 original_recipient: wietse + 211 recipient: wietse@ahost.example.com + 237 pointer_record: 0 + 254 *** MESSAGE CONTENTS test-queue-file14b.tmp *** + 256 regular_text: Received: by ahost.example.com (Postfix, from userid 1001) + 316 regular_text: id DA4892510C1; Fri, 5 Jun 2009 14:05:19 -0400 (EDT) + 372 regular_text: To: wietse@ahost.example.com + 402 regular_text: Message-Id: <20090605180519.DA4892510C1@ahost.example.com> + 462 regular_text: Date: Fri, 5 Jun 2009 14:05:19 -0400 (EDT) + 507 regular_text: From: wietse@ahost.example.com (Wietse Venema) + 555 pointer_record: 642 + 642 regular_text: X-SPAM-FLAG: YES + 660 pointer_record: 572 + 572 regular_text: + 574 regular_text: Fri Jun 5 14:05:19 EDT 2009 + 604 pointer_record: 0 + 621 *** HEADER EXTRACTED test-queue-file14b.tmp *** + 623 pointer_record: 677 + 677 content_filter: transport:nexthop:port + 701 pointer_record: 640 + 640 *** MESSAGE FILE END test-queue-file14b.tmp *** diff --git a/src/cleanup/cleanup_milter.ref14c1 b/src/cleanup/cleanup_milter.ref14c1 new file mode 100644 index 0000000..26b8ffd --- /dev/null +++ b/src/cleanup/cleanup_milter.ref14c1 @@ -0,0 +1 @@ +./cleanup_milter: NOQUEUE: milter-header-redirect: header X-SPAM-FLAG: YES from client_name[client_addr]; from=<sender> to=<recipient>: foo@examle.com diff --git a/src/cleanup/cleanup_milter.ref14c2 b/src/cleanup/cleanup_milter.ref14c2 new file mode 100644 index 0000000..8d2be31 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref14c2 @@ -0,0 +1,29 @@ +*** ENVELOPE RECORDS test-queue-file14c.tmp *** + 0 message_size: 365 256 1 0 365 + 81 message_arrival_time: Fri Jun 5 14:05:19 2009 + 100 create_time: Fri Jun 5 14:05:19 2009 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: wietse@ahost.example.com + 188 pointer_record: 0 + 203 original_recipient: wietse + 211 recipient: wietse@ahost.example.com + 237 pointer_record: 0 + 254 *** MESSAGE CONTENTS test-queue-file14c.tmp *** + 256 regular_text: Received: by ahost.example.com (Postfix, from userid 1001) + 316 regular_text: id DA4892510C1; Fri, 5 Jun 2009 14:05:19 -0400 (EDT) + 372 regular_text: To: wietse@ahost.example.com + 402 regular_text: Message-Id: <20090605180519.DA4892510C1@ahost.example.com> + 462 regular_text: Date: Fri, 5 Jun 2009 14:05:19 -0400 (EDT) + 507 regular_text: From: wietse@ahost.example.com (Wietse Venema) + 555 pointer_record: 642 + 642 regular_text: X-SPAM-FLAG: YES + 660 pointer_record: 572 + 572 regular_text: + 574 regular_text: Fri Jun 5 14:05:19 EDT 2009 + 604 pointer_record: 0 + 621 *** HEADER EXTRACTED test-queue-file14c.tmp *** + 623 pointer_record: 677 + 677 redirect_to: foo@examle.com + 693 pointer_record: 640 + 640 *** MESSAGE FILE END test-queue-file14c.tmp *** diff --git a/src/cleanup/cleanup_milter.ref14d1 b/src/cleanup/cleanup_milter.ref14d1 new file mode 100644 index 0000000..2cea8d2 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref14d1 @@ -0,0 +1,2 @@ +./cleanup_milter: NOQUEUE: milter-header-discard: header X-SPAM-FLAG: YES from client_name[client_addr]; from=<sender> to=<recipient>: +./cleanup_milter: flags = discard_message diff --git a/src/cleanup/cleanup_milter.ref14d2 b/src/cleanup/cleanup_milter.ref14d2 new file mode 100644 index 0000000..dece3d5 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref14d2 @@ -0,0 +1,25 @@ +*** ENVELOPE RECORDS test-queue-file14d.tmp *** + 0 message_size: 365 256 1 0 365 + 81 message_arrival_time: Fri Jun 5 14:05:19 2009 + 100 create_time: Fri Jun 5 14:05:19 2009 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: wietse@ahost.example.com + 188 pointer_record: 0 + 203 original_recipient: wietse + 211 recipient: wietse@ahost.example.com + 237 pointer_record: 0 + 254 *** MESSAGE CONTENTS test-queue-file14d.tmp *** + 256 regular_text: Received: by ahost.example.com (Postfix, from userid 1001) + 316 regular_text: id DA4892510C1; Fri, 5 Jun 2009 14:05:19 -0400 (EDT) + 372 regular_text: To: wietse@ahost.example.com + 402 regular_text: Message-Id: <20090605180519.DA4892510C1@ahost.example.com> + 462 regular_text: Date: Fri, 5 Jun 2009 14:05:19 -0400 (EDT) + 507 regular_text: From: wietse@ahost.example.com (Wietse Venema) + 555 pointer_record: 0 + 572 regular_text: + 574 regular_text: Fri Jun 5 14:05:19 EDT 2009 + 604 pointer_record: 0 + 621 *** HEADER EXTRACTED test-queue-file14d.tmp *** + 623 pointer_record: 0 + 640 *** MESSAGE FILE END test-queue-file14d.tmp *** diff --git a/src/cleanup/cleanup_milter.ref14e1 b/src/cleanup/cleanup_milter.ref14e1 new file mode 100644 index 0000000..d08c06f --- /dev/null +++ b/src/cleanup/cleanup_milter.ref14e1 @@ -0,0 +1,2 @@ +./cleanup_milter: NOQUEUE: milter-header-hold: header X-SPAM-FLAG: YES from client_name[client_addr]; from=<sender> to=<recipient>: +./cleanup_milter: flags = enable_header_body_filter hold_message enable_milters diff --git a/src/cleanup/cleanup_milter.ref14e2 b/src/cleanup/cleanup_milter.ref14e2 new file mode 100644 index 0000000..e6e5cc0 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref14e2 @@ -0,0 +1,27 @@ +*** ENVELOPE RECORDS test-queue-file14e.tmp *** + 0 message_size: 365 256 1 0 365 + 81 message_arrival_time: Fri Jun 5 14:05:19 2009 + 100 create_time: Fri Jun 5 14:05:19 2009 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: wietse@ahost.example.com + 188 pointer_record: 0 + 203 original_recipient: wietse + 211 recipient: wietse@ahost.example.com + 237 pointer_record: 0 + 254 *** MESSAGE CONTENTS test-queue-file14e.tmp *** + 256 regular_text: Received: by ahost.example.com (Postfix, from userid 1001) + 316 regular_text: id DA4892510C1; Fri, 5 Jun 2009 14:05:19 -0400 (EDT) + 372 regular_text: To: wietse@ahost.example.com + 402 regular_text: Message-Id: <20090605180519.DA4892510C1@ahost.example.com> + 462 regular_text: Date: Fri, 5 Jun 2009 14:05:19 -0400 (EDT) + 507 regular_text: From: wietse@ahost.example.com (Wietse Venema) + 555 pointer_record: 642 + 642 regular_text: X-SPAM-FLAG: YES + 660 pointer_record: 572 + 572 regular_text: + 574 regular_text: Fri Jun 5 14:05:19 EDT 2009 + 604 pointer_record: 0 + 621 *** HEADER EXTRACTED test-queue-file14e.tmp *** + 623 pointer_record: 0 + 640 *** MESSAGE FILE END test-queue-file14e.tmp *** diff --git a/src/cleanup/cleanup_milter.ref14f1 b/src/cleanup/cleanup_milter.ref14f1 new file mode 100644 index 0000000..9006f9a --- /dev/null +++ b/src/cleanup/cleanup_milter.ref14f1 @@ -0,0 +1,2 @@ +./cleanup_milter: NOQUEUE: milter-header-reject: header X-SPAM-FLAG: YES from client_name[client_addr]; from=<sender> to=<recipient>: 5.7.1 message content rejected +./cleanup_milter: errs = message content rejected diff --git a/src/cleanup/cleanup_milter.ref14f2 b/src/cleanup/cleanup_milter.ref14f2 new file mode 100644 index 0000000..3fdbf23 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref14f2 @@ -0,0 +1,28 @@ +*** ENVELOPE RECORDS test-queue-file14f.tmp *** + 0 message_size: 365 256 1 0 365 + 81 message_arrival_time: Fri Jun 5 14:05:19 2009 + 100 create_time: Fri Jun 5 14:05:19 2009 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: wietse@ahost.example.com + 188 pointer_record: 0 + 203 original_recipient: wietse + 211 recipient: wietse@ahost.example.com + 237 pointer_record: 0 + 254 *** MESSAGE CONTENTS test-queue-file14f.tmp *** + 256 regular_text: Received: by ahost.example.com (Postfix, from userid 1001) + 316 regular_text: id DA4892510C1; Fri, 5 Jun 2009 14:05:19 -0400 (EDT) + 372 pointer_record: 642 + 642 regular_text: X-SPAM-FLAG: YES + 660 regular_text: To: wietse@ahost.example.com + 690 pointer_record: 402 + 402 regular_text: Message-Id: <20090605180519.DA4892510C1@ahost.example.com> + 462 regular_text: Date: Fri, 5 Jun 2009 14:05:19 -0400 (EDT) + 507 regular_text: From: wietse@ahost.example.com (Wietse Venema) + 555 pointer_record: 0 + 572 regular_text: + 574 regular_text: Fri Jun 5 14:05:19 EDT 2009 + 604 pointer_record: 0 + 621 *** HEADER EXTRACTED test-queue-file14f.tmp *** + 623 pointer_record: 0 + 640 *** MESSAGE FILE END test-queue-file14f.tmp *** diff --git a/src/cleanup/cleanup_milter.ref14g1 b/src/cleanup/cleanup_milter.ref14g1 new file mode 100644 index 0000000..9006f9a --- /dev/null +++ b/src/cleanup/cleanup_milter.ref14g1 @@ -0,0 +1,2 @@ +./cleanup_milter: NOQUEUE: milter-header-reject: header X-SPAM-FLAG: YES from client_name[client_addr]; from=<sender> to=<recipient>: 5.7.1 message content rejected +./cleanup_milter: errs = message content rejected diff --git a/src/cleanup/cleanup_milter.ref14g2 b/src/cleanup/cleanup_milter.ref14g2 new file mode 100644 index 0000000..1ee50e4 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref14g2 @@ -0,0 +1,27 @@ +*** ENVELOPE RECORDS test-queue-file14g.tmp *** + 0 message_size: 365 256 1 0 365 + 81 message_arrival_time: Fri Jun 5 14:05:19 2009 + 100 create_time: Fri Jun 5 14:05:19 2009 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: wietse@ahost.example.com + 188 pointer_record: 0 + 203 original_recipient: wietse + 211 recipient: wietse@ahost.example.com + 237 pointer_record: 0 + 254 *** MESSAGE CONTENTS test-queue-file14g.tmp *** + 256 regular_text: Received: by ahost.example.com (Postfix, from userid 1001) + 316 regular_text: id DA4892510C1; Fri, 5 Jun 2009 14:05:19 -0400 (EDT) + 372 regular_text: To: wietse@ahost.example.com + 402 regular_text: Message-Id: <20090605180519.DA4892510C1@ahost.example.com> + 462 regular_text: Date: Fri, 5 Jun 2009 14:05:19 -0400 (EDT) + 507 regular_text: From: wietse@ahost.example.com (Wietse Venema) + 555 pointer_record: 642 + 642 regular_text: X-SPAM-FLAG: YES + 660 pointer_record: 572 + 572 regular_text: + 574 regular_text: Fri Jun 5 14:05:19 EDT 2009 + 604 pointer_record: 0 + 621 *** HEADER EXTRACTED test-queue-file14g.tmp *** + 623 pointer_record: 0 + 640 *** MESSAGE FILE END test-queue-file14g.tmp *** diff --git a/src/cleanup/cleanup_milter.ref15a1 b/src/cleanup/cleanup_milter.ref15a1 new file mode 100644 index 0000000..9006f9a --- /dev/null +++ b/src/cleanup/cleanup_milter.ref15a1 @@ -0,0 +1,2 @@ +./cleanup_milter: NOQUEUE: milter-header-reject: header X-SPAM-FLAG: YES from client_name[client_addr]; from=<sender> to=<recipient>: 5.7.1 message content rejected +./cleanup_milter: errs = message content rejected diff --git a/src/cleanup/cleanup_milter.ref15a2 b/src/cleanup/cleanup_milter.ref15a2 new file mode 100644 index 0000000..56c5d3e --- /dev/null +++ b/src/cleanup/cleanup_milter.ref15a2 @@ -0,0 +1,27 @@ +*** ENVELOPE RECORDS test-queue-file15a.tmp *** + 0 message_size: 365 221 1 0 365 + 81 message_arrival_time: Fri Jun 5 14:06:34 2009 + 99 create_time: Fri Jun 5 14:06:34 2009 + 123 named_attribute: rewrite_context=local + 146 sender_fullname: Wietse Venema + 161 sender: wietse@ahost.example.com + 187 pointer_record: 0 + 202 pointer_record: 0 + 219 *** MESSAGE CONTENTS test-queue-file15a.tmp *** + 221 regular_text: Received: by ahost.example.com (Postfix, from userid 1001) + 281 regular_text: id 06F8B2510C2; Fri, 5 Jun 2009 14:06:34 -0400 (EDT) + 337 regular_text: To: wietse@ahost.example.com + 367 regular_text: Message-Id: <20090605180634.06F8B2510C2@ahost.example.com> + 427 regular_text: Date: Fri, 5 Jun 2009 14:06:34 -0400 (EDT) + 472 regular_text: From: wietse@ahost.example.com (Wietse Venema) + 520 pointer_record: 641 + 641 regular_text: X-SPAM-FLAG: YES + 659 pointer_record: 537 + 537 regular_text: + 539 regular_text: Fri Jun 5 14:06:34 EDT 2009 + 569 pointer_record: 0 + 586 *** HEADER EXTRACTED test-queue-file15a.tmp *** + 588 pointer_record: 0 + 605 original_recipient: wietse + 613 recipient: wietse@ahost.example.com + 639 *** MESSAGE FILE END test-queue-file15a.tmp *** diff --git a/src/cleanup/cleanup_milter.ref15b1 b/src/cleanup/cleanup_milter.ref15b1 new file mode 100644 index 0000000..5608b81 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref15b1 @@ -0,0 +1,2 @@ +./cleanup_milter: NOQUEUE: milter-header-filter: header X-SPAM-FLAG: YES from client_name[client_addr]; from=<sender> to=<recipient>: transport:nexthop:port +./cleanup_milter: flags = enable_header_body_filter enable_milters diff --git a/src/cleanup/cleanup_milter.ref15b2 b/src/cleanup/cleanup_milter.ref15b2 new file mode 100644 index 0000000..c38c0a3 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref15b2 @@ -0,0 +1,29 @@ +*** ENVELOPE RECORDS test-queue-file15b.tmp *** + 0 message_size: 365 221 1 0 365 + 81 message_arrival_time: Fri Jun 5 14:06:34 2009 + 99 create_time: Fri Jun 5 14:06:34 2009 + 123 named_attribute: rewrite_context=local + 146 sender_fullname: Wietse Venema + 161 sender: wietse@ahost.example.com + 187 pointer_record: 0 + 202 pointer_record: 0 + 219 *** MESSAGE CONTENTS test-queue-file15b.tmp *** + 221 regular_text: Received: by ahost.example.com (Postfix, from userid 1001) + 281 regular_text: id 06F8B2510C2; Fri, 5 Jun 2009 14:06:34 -0400 (EDT) + 337 regular_text: To: wietse@ahost.example.com + 367 regular_text: Message-Id: <20090605180634.06F8B2510C2@ahost.example.com> + 427 regular_text: Date: Fri, 5 Jun 2009 14:06:34 -0400 (EDT) + 472 regular_text: From: wietse@ahost.example.com (Wietse Venema) + 520 pointer_record: 641 + 641 regular_text: X-SPAM-FLAG: YES + 659 pointer_record: 537 + 537 regular_text: + 539 regular_text: Fri Jun 5 14:06:34 EDT 2009 + 569 pointer_record: 0 + 586 *** HEADER EXTRACTED test-queue-file15b.tmp *** + 588 pointer_record: 676 + 676 content_filter: transport:nexthop:port + 700 pointer_record: 605 + 605 original_recipient: wietse + 613 recipient: wietse@ahost.example.com + 639 *** MESSAGE FILE END test-queue-file15b.tmp *** diff --git a/src/cleanup/cleanup_milter.ref15c1 b/src/cleanup/cleanup_milter.ref15c1 new file mode 100644 index 0000000..26b8ffd --- /dev/null +++ b/src/cleanup/cleanup_milter.ref15c1 @@ -0,0 +1 @@ +./cleanup_milter: NOQUEUE: milter-header-redirect: header X-SPAM-FLAG: YES from client_name[client_addr]; from=<sender> to=<recipient>: foo@examle.com diff --git a/src/cleanup/cleanup_milter.ref15c2 b/src/cleanup/cleanup_milter.ref15c2 new file mode 100644 index 0000000..7725f48 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref15c2 @@ -0,0 +1,29 @@ +*** ENVELOPE RECORDS test-queue-file15c.tmp *** + 0 message_size: 365 221 1 0 365 + 81 message_arrival_time: Fri Jun 5 14:06:34 2009 + 99 create_time: Fri Jun 5 14:06:34 2009 + 123 named_attribute: rewrite_context=local + 146 sender_fullname: Wietse Venema + 161 sender: wietse@ahost.example.com + 187 pointer_record: 0 + 202 pointer_record: 0 + 219 *** MESSAGE CONTENTS test-queue-file15c.tmp *** + 221 regular_text: Received: by ahost.example.com (Postfix, from userid 1001) + 281 regular_text: id 06F8B2510C2; Fri, 5 Jun 2009 14:06:34 -0400 (EDT) + 337 regular_text: To: wietse@ahost.example.com + 367 regular_text: Message-Id: <20090605180634.06F8B2510C2@ahost.example.com> + 427 regular_text: Date: Fri, 5 Jun 2009 14:06:34 -0400 (EDT) + 472 regular_text: From: wietse@ahost.example.com (Wietse Venema) + 520 pointer_record: 641 + 641 regular_text: X-SPAM-FLAG: YES + 659 pointer_record: 537 + 537 regular_text: + 539 regular_text: Fri Jun 5 14:06:34 EDT 2009 + 569 pointer_record: 0 + 586 *** HEADER EXTRACTED test-queue-file15c.tmp *** + 588 pointer_record: 676 + 676 redirect_to: foo@examle.com + 692 pointer_record: 605 + 605 original_recipient: wietse + 613 recipient: wietse@ahost.example.com + 639 *** MESSAGE FILE END test-queue-file15c.tmp *** diff --git a/src/cleanup/cleanup_milter.ref15d1 b/src/cleanup/cleanup_milter.ref15d1 new file mode 100644 index 0000000..2cea8d2 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref15d1 @@ -0,0 +1,2 @@ +./cleanup_milter: NOQUEUE: milter-header-discard: header X-SPAM-FLAG: YES from client_name[client_addr]; from=<sender> to=<recipient>: +./cleanup_milter: flags = discard_message diff --git a/src/cleanup/cleanup_milter.ref15d2 b/src/cleanup/cleanup_milter.ref15d2 new file mode 100644 index 0000000..746a13b --- /dev/null +++ b/src/cleanup/cleanup_milter.ref15d2 @@ -0,0 +1,25 @@ +*** ENVELOPE RECORDS test-queue-file15d.tmp *** + 0 message_size: 365 221 1 0 365 + 81 message_arrival_time: Fri Jun 5 14:06:34 2009 + 99 create_time: Fri Jun 5 14:06:34 2009 + 123 named_attribute: rewrite_context=local + 146 sender_fullname: Wietse Venema + 161 sender: wietse@ahost.example.com + 187 pointer_record: 0 + 202 pointer_record: 0 + 219 *** MESSAGE CONTENTS test-queue-file15d.tmp *** + 221 regular_text: Received: by ahost.example.com (Postfix, from userid 1001) + 281 regular_text: id 06F8B2510C2; Fri, 5 Jun 2009 14:06:34 -0400 (EDT) + 337 regular_text: To: wietse@ahost.example.com + 367 regular_text: Message-Id: <20090605180634.06F8B2510C2@ahost.example.com> + 427 regular_text: Date: Fri, 5 Jun 2009 14:06:34 -0400 (EDT) + 472 regular_text: From: wietse@ahost.example.com (Wietse Venema) + 520 pointer_record: 0 + 537 regular_text: + 539 regular_text: Fri Jun 5 14:06:34 EDT 2009 + 569 pointer_record: 0 + 586 *** HEADER EXTRACTED test-queue-file15d.tmp *** + 588 pointer_record: 0 + 605 original_recipient: wietse + 613 recipient: wietse@ahost.example.com + 639 *** MESSAGE FILE END test-queue-file15d.tmp *** diff --git a/src/cleanup/cleanup_milter.ref15e1 b/src/cleanup/cleanup_milter.ref15e1 new file mode 100644 index 0000000..d08c06f --- /dev/null +++ b/src/cleanup/cleanup_milter.ref15e1 @@ -0,0 +1,2 @@ +./cleanup_milter: NOQUEUE: milter-header-hold: header X-SPAM-FLAG: YES from client_name[client_addr]; from=<sender> to=<recipient>: +./cleanup_milter: flags = enable_header_body_filter hold_message enable_milters diff --git a/src/cleanup/cleanup_milter.ref15e2 b/src/cleanup/cleanup_milter.ref15e2 new file mode 100644 index 0000000..13f9cb4 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref15e2 @@ -0,0 +1,27 @@ +*** ENVELOPE RECORDS test-queue-file15e.tmp *** + 0 message_size: 365 221 1 0 365 + 81 message_arrival_time: Fri Jun 5 14:06:34 2009 + 99 create_time: Fri Jun 5 14:06:34 2009 + 123 named_attribute: rewrite_context=local + 146 sender_fullname: Wietse Venema + 161 sender: wietse@ahost.example.com + 187 pointer_record: 0 + 202 pointer_record: 0 + 219 *** MESSAGE CONTENTS test-queue-file15e.tmp *** + 221 regular_text: Received: by ahost.example.com (Postfix, from userid 1001) + 281 regular_text: id 06F8B2510C2; Fri, 5 Jun 2009 14:06:34 -0400 (EDT) + 337 regular_text: To: wietse@ahost.example.com + 367 regular_text: Message-Id: <20090605180634.06F8B2510C2@ahost.example.com> + 427 regular_text: Date: Fri, 5 Jun 2009 14:06:34 -0400 (EDT) + 472 regular_text: From: wietse@ahost.example.com (Wietse Venema) + 520 pointer_record: 641 + 641 regular_text: X-SPAM-FLAG: YES + 659 pointer_record: 537 + 537 regular_text: + 539 regular_text: Fri Jun 5 14:06:34 EDT 2009 + 569 pointer_record: 0 + 586 *** HEADER EXTRACTED test-queue-file15e.tmp *** + 588 pointer_record: 0 + 605 original_recipient: wietse + 613 recipient: wietse@ahost.example.com + 639 *** MESSAGE FILE END test-queue-file15e.tmp *** diff --git a/src/cleanup/cleanup_milter.ref15f1 b/src/cleanup/cleanup_milter.ref15f1 new file mode 100644 index 0000000..b9d6021 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref15f1 @@ -0,0 +1 @@ +./cleanup_milter: NOQUEUE: milter-header-redirect: header X-SPAM-FLAG: YES from client_name[client_addr]; from=<sender> to=<recipient>: x@y.z diff --git a/src/cleanup/cleanup_milter.ref15f2 b/src/cleanup/cleanup_milter.ref15f2 new file mode 100644 index 0000000..45dca53 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref15f2 @@ -0,0 +1,30 @@ +*** ENVELOPE RECORDS test-queue-file15f.tmp *** + 0 message_size: 365 221 1 0 365 + 81 message_arrival_time: Fri Jun 5 14:06:34 2009 + 99 create_time: Fri Jun 5 14:06:34 2009 + 123 named_attribute: rewrite_context=local + 146 sender_fullname: Wietse Venema + 161 sender: wietse@ahost.example.com + 187 pointer_record: 0 + 202 pointer_record: 0 + 219 *** MESSAGE CONTENTS test-queue-file15f.tmp *** + 221 regular_text: Received: by ahost.example.com (Postfix, from userid 1001) + 281 regular_text: id 06F8B2510C2; Fri, 5 Jun 2009 14:06:34 -0400 (EDT) + 337 pointer_record: 641 + 641 regular_text: X-SPAM-FLAG: YES + 659 regular_text: To: wietse@ahost.example.com + 689 pointer_record: 367 + 367 regular_text: Message-Id: <20090605180634.06F8B2510C2@ahost.example.com> + 427 regular_text: Date: Fri, 5 Jun 2009 14:06:34 -0400 (EDT) + 472 regular_text: From: wietse@ahost.example.com (Wietse Venema) + 520 pointer_record: 0 + 537 regular_text: + 539 regular_text: Fri Jun 5 14:06:34 EDT 2009 + 569 pointer_record: 0 + 586 *** HEADER EXTRACTED test-queue-file15f.tmp *** + 588 pointer_record: 706 + 706 redirect_to: x@y.z + 713 pointer_record: 605 + 605 original_recipient: wietse + 613 recipient: wietse@ahost.example.com + 639 *** MESSAGE FILE END test-queue-file15f.tmp *** diff --git a/src/cleanup/cleanup_milter.ref15g1 b/src/cleanup/cleanup_milter.ref15g1 new file mode 100644 index 0000000..295690a --- /dev/null +++ b/src/cleanup/cleanup_milter.ref15g1 @@ -0,0 +1,2 @@ +./cleanup_milter: NOQUEUE: milter-header-filter: header X-SPAM-FLAG: YES from client_name[client_addr]; from=<sender> to=<recipient>: x:y:z +./cleanup_milter: flags = enable_header_body_filter enable_milters diff --git a/src/cleanup/cleanup_milter.ref15g2 b/src/cleanup/cleanup_milter.ref15g2 new file mode 100644 index 0000000..fc67e56 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref15g2 @@ -0,0 +1,30 @@ +*** ENVELOPE RECORDS test-queue-file15g.tmp *** + 0 message_size: 365 221 1 0 365 + 81 message_arrival_time: Fri Jun 5 14:06:34 2009 + 99 create_time: Fri Jun 5 14:06:34 2009 + 123 named_attribute: rewrite_context=local + 146 sender_fullname: Wietse Venema + 161 sender: wietse@ahost.example.com + 187 pointer_record: 0 + 202 pointer_record: 0 + 219 *** MESSAGE CONTENTS test-queue-file15g.tmp *** + 221 regular_text: Received: by ahost.example.com (Postfix, from userid 1001) + 281 regular_text: id 06F8B2510C2; Fri, 5 Jun 2009 14:06:34 -0400 (EDT) + 337 pointer_record: 641 + 641 regular_text: X-SPAM-FLAG: YES + 659 regular_text: To: wietse@ahost.example.com + 689 pointer_record: 367 + 367 regular_text: Message-Id: <20090605180634.06F8B2510C2@ahost.example.com> + 427 regular_text: Date: Fri, 5 Jun 2009 14:06:34 -0400 (EDT) + 472 regular_text: From: wietse@ahost.example.com (Wietse Venema) + 520 pointer_record: 0 + 537 regular_text: + 539 regular_text: Fri Jun 5 14:06:34 EDT 2009 + 569 pointer_record: 0 + 586 *** HEADER EXTRACTED test-queue-file15g.tmp *** + 588 pointer_record: 706 + 706 content_filter: x:y:z + 713 pointer_record: 605 + 605 original_recipient: wietse + 613 recipient: wietse@ahost.example.com + 639 *** MESSAGE FILE END test-queue-file15g.tmp *** diff --git a/src/cleanup/cleanup_milter.ref15h1 b/src/cleanup/cleanup_milter.ref15h1 new file mode 100644 index 0000000..bb51e0e --- /dev/null +++ b/src/cleanup/cleanup_milter.ref15h1 @@ -0,0 +1,3 @@ +./cleanup_milter: NOQUEUE: milter-header-reject: header X-SPAM-FLAG: YES from client_name[client_addr]; from=<sender> to=<recipient>: 5.7.1 whatever +./cleanup_milter: ignoring: add_header X-SPAM-FLAG NO +./cleanup_milter: errs = message content rejected diff --git a/src/cleanup/cleanup_milter.ref15h2 b/src/cleanup/cleanup_milter.ref15h2 new file mode 100644 index 0000000..936f022 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref15h2 @@ -0,0 +1,27 @@ +*** ENVELOPE RECORDS test-queue-file15h.tmp *** + 0 message_size: 365 221 1 0 365 + 81 message_arrival_time: Fri Jun 5 14:06:34 2009 + 99 create_time: Fri Jun 5 14:06:34 2009 + 123 named_attribute: rewrite_context=local + 146 sender_fullname: Wietse Venema + 161 sender: wietse@ahost.example.com + 187 pointer_record: 0 + 202 pointer_record: 0 + 219 *** MESSAGE CONTENTS test-queue-file15h.tmp *** + 221 regular_text: Received: by ahost.example.com (Postfix, from userid 1001) + 281 regular_text: id 06F8B2510C2; Fri, 5 Jun 2009 14:06:34 -0400 (EDT) + 337 regular_text: To: wietse@ahost.example.com + 367 regular_text: Message-Id: <20090605180634.06F8B2510C2@ahost.example.com> + 427 regular_text: Date: Fri, 5 Jun 2009 14:06:34 -0400 (EDT) + 472 regular_text: From: wietse@ahost.example.com (Wietse Venema) + 520 pointer_record: 641 + 641 regular_text: X-SPAM-FLAG: YES + 659 pointer_record: 537 + 537 regular_text: + 539 regular_text: Fri Jun 5 14:06:34 EDT 2009 + 569 pointer_record: 0 + 586 *** HEADER EXTRACTED test-queue-file15h.tmp *** + 588 pointer_record: 0 + 605 original_recipient: wietse + 613 recipient: wietse@ahost.example.com + 639 *** MESSAGE FILE END test-queue-file15h.tmp *** diff --git a/src/cleanup/cleanup_milter.ref15i1 b/src/cleanup/cleanup_milter.ref15i1 new file mode 100644 index 0000000..b561728 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref15i1 @@ -0,0 +1,3 @@ +./cleanup_milter: NOQUEUE: milter-header-filter: header X-SPAM-FLAG: NO from client_name[client_addr]; from=<sender> to=<recipient>: x:y:z +./cleanup_milter: NOQUEUE: milter-header-reject: header X-SPAM-FLAG: YES from client_name[client_addr]; from=<sender> to=<recipient>: 5.7.1 whatever +./cleanup_milter: errs = message content rejected diff --git a/src/cleanup/cleanup_milter.ref15i2 b/src/cleanup/cleanup_milter.ref15i2 new file mode 100644 index 0000000..531ac4c --- /dev/null +++ b/src/cleanup/cleanup_milter.ref15i2 @@ -0,0 +1,29 @@ +*** ENVELOPE RECORDS test-queue-file15i.tmp *** + 0 message_size: 365 221 1 0 365 + 81 message_arrival_time: Fri Jun 5 14:06:34 2009 + 99 create_time: Fri Jun 5 14:06:34 2009 + 123 named_attribute: rewrite_context=local + 146 sender_fullname: Wietse Venema + 161 sender: wietse@ahost.example.com + 187 pointer_record: 0 + 202 pointer_record: 0 + 219 *** MESSAGE CONTENTS test-queue-file15i.tmp *** + 221 regular_text: Received: by ahost.example.com (Postfix, from userid 1001) + 281 regular_text: id 06F8B2510C2; Fri, 5 Jun 2009 14:06:34 -0400 (EDT) + 337 regular_text: To: wietse@ahost.example.com + 367 regular_text: Message-Id: <20090605180634.06F8B2510C2@ahost.example.com> + 427 regular_text: Date: Fri, 5 Jun 2009 14:06:34 -0400 (EDT) + 472 regular_text: From: wietse@ahost.example.com (Wietse Venema) + 520 pointer_record: 641 + 641 regular_text: X-SPAM-FLAG: NO + 658 pointer_record: 675 + 675 regular_text: X-SPAM-FLAG: YES + 693 pointer_record: 537 + 537 regular_text: + 539 regular_text: Fri Jun 5 14:06:34 EDT 2009 + 569 pointer_record: 0 + 586 *** HEADER EXTRACTED test-queue-file15i.tmp *** + 588 pointer_record: 0 + 605 original_recipient: wietse + 613 recipient: wietse@ahost.example.com + 639 *** MESSAGE FILE END test-queue-file15i.tmp *** diff --git a/src/cleanup/cleanup_milter.ref16a1 b/src/cleanup/cleanup_milter.ref16a1 new file mode 100644 index 0000000..a1d30b0 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref16a1 @@ -0,0 +1,3 @@ +./cleanup_milter: NOQUEUE: milter-header-bcc: header X-SPAM-FLAG: NO from client_name[client_addr]; from=<sender> to=<recipient>: bar@example.com +./cleanup_milter: NOQUEUE: milter-header-bcc: header X-SPAM-FLAG: YES from client_name[client_addr]; from=<sender> to=<recipient>: foo@example.com +./cleanup_milter: flags = enable_header_body_filter enable_milters diff --git a/src/cleanup/cleanup_milter.ref16a2 b/src/cleanup/cleanup_milter.ref16a2 new file mode 100644 index 0000000..3b0edd2 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref16a2 @@ -0,0 +1,37 @@ +*** ENVELOPE RECORDS test-queue-file16a.tmp *** + 0 message_size: 343 215 1 0 343 0 + 97 message_arrival_time: Tue Nov 18 16:43:29 2014 + 116 create_time: Tue Nov 18 16:43:29 2014 + 140 named_attribute: rewrite_context=local + 163 sender_fullname: Wietse Venema + 178 sender: user@example.com + 196 pointer_record: 654 + 654 original_recipient: bar@example.com + 671 recipient: bar@example.com + 688 pointer_record: 739 + 739 original_recipient: foo@example.com + 756 recipient: foo@example.com + 773 pointer_record: 213 + 213 *** MESSAGE CONTENTS test-queue-file16a.tmp *** + 215 regular_text: Received: by host.example.com (Postfix, from userid 1001) + 274 regular_text: id 663E22172797; Tue, 18 Nov 2014 16:43:29 -0500 (EST) + 331 regular_text: To: user@example.com + 353 regular_text: Subject: test + 368 padding: 0 + 371 regular_text: Message-Id: <20141118214329.663E22172797@host.example.com> + 431 regular_text: Date: Tue, 18 Nov 2014 16:43:29 -0500 (EST) + 476 regular_text: From: user@example.com (Wietse Venema) + 516 pointer_record: 705 + 705 regular_text: X-SPAM-FLAG: NO + 722 pointer_record: 790 + 790 regular_text: X-SPAM-FLAG: YES + 808 pointer_record: 533 + 533 regular_text: + 535 regular_text: test + 541 pointer_record: 0 + 558 *** HEADER EXTRACTED test-queue-file16a.tmp *** + 560 pointer_record: 0 + 577 named_attribute: dsn_orig_rcpt=rfc822;user@example.com + 616 original_recipient: user@example.com + 634 recipient: user@example.com + 652 *** MESSAGE FILE END test-queue-file16a.tmp *** diff --git a/src/cleanup/cleanup_milter.ref16b1 b/src/cleanup/cleanup_milter.ref16b1 new file mode 100644 index 0000000..eab5a83 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref16b1 @@ -0,0 +1 @@ +./cleanup_milter: flags = enable_header_body_filter enable_milters diff --git a/src/cleanup/cleanup_milter.ref16b2 b/src/cleanup/cleanup_milter.ref16b2 new file mode 100644 index 0000000..2ae8719 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref16b2 @@ -0,0 +1,42 @@ +*** ENVELOPE RECORDS test-queue-file16b.tmp *** + 0 message_size: 343 215 1 0 343 0 + 97 message_arrival_time: Tue Nov 18 16:43:29 2014 + 116 create_time: Tue Nov 18 16:43:29 2014 + 140 named_attribute: rewrite_context=local + 163 sender_fullname: Wietse Venema + 178 sender: user@example.com + 196 pointer_record: 654 + 654 named_attribute: notify_flags=1 + 670 original_recipient: foo@example.com + 687 recipient: foo@example.com + 704 pointer_record: 721 + 721 original_recipient: bar@example.com + 738 recipient: bar@example.com + 755 pointer_record: 772 + 772 named_attribute: dsn_orig_rcpt=rfc822;orig-bar@example.com + 815 original_recipient: bar@example.com + 832 recipient: bar@example.com + 849 pointer_record: 866 + 866 named_attribute: notify_flags=8 + 882 original_recipient: bar@example.com + 899 recipient: bar@example.com + 916 pointer_record: 213 + 213 *** MESSAGE CONTENTS test-queue-file16b.tmp *** + 215 regular_text: Received: by host.example.com (Postfix, from userid 1001) + 274 regular_text: id 663E22172797; Tue, 18 Nov 2014 16:43:29 -0500 (EST) + 331 regular_text: To: user@example.com + 353 regular_text: Subject: test + 368 padding: 0 + 371 regular_text: Message-Id: <20141118214329.663E22172797@host.example.com> + 431 regular_text: Date: Tue, 18 Nov 2014 16:43:29 -0500 (EST) + 476 regular_text: From: user@example.com (Wietse Venema) + 516 pointer_record: 0 + 533 regular_text: + 535 regular_text: test + 541 pointer_record: 0 + 558 *** HEADER EXTRACTED test-queue-file16b.tmp *** + 560 pointer_record: 0 + 577 named_attribute: dsn_orig_rcpt=rfc822;user@example.com + 616 original_recipient: user@example.com + 634 recipient: user@example.com + 652 *** MESSAGE FILE END test-queue-file16b.tmp *** diff --git a/src/cleanup/cleanup_milter.ref2 b/src/cleanup/cleanup_milter.ref2 new file mode 100644 index 0000000..bdfc994 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref2 @@ -0,0 +1,36 @@ +*** ENVELOPE RECORDS test-queue-file2.tmp *** + 0 message_size: 332 199 1 0 332 + 81 message_arrival_time: Sat Jan 20 20:53:54 2007 + 100 create_time: Sat Jan 20 20:53:59 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: me@porcupine.org + 180 pointer_record: 0 + 197 *** MESSAGE CONTENTS test-queue-file2.tmp *** + 199 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 261 regular_text: id B85F1290407; Sat, 20 Jan 2007 20:53:59 -0500 (EST) + 317 regular_text: From: me@porcupine.org + 341 regular_text: To: you@porcupine.org + 364 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 426 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 471 pointer_record: 573 + 573 pointer_record: 608 + 608 pointer_record: 643 + 643 regular_text: Subject: hey! + 658 padding: 0 + 661 pointer_record: 489 + 489 pointer_record: 678 + 678 pointer_record: 712 + 712 pointer_record: 746 + 746 pointer_record: 780 + 780 regular_text: foo: foobar + 793 padding: 0 + 797 pointer_record: 695 + 695 pointer_record: 506 + 506 regular_text: + 508 regular_text: text + 514 pointer_record: 0 + 531 *** HEADER EXTRACTED test-queue-file2.tmp *** + 533 original_recipient: you@porcupine.org + 552 recipient: you@porcupine.org + 571 *** MESSAGE FILE END test-queue-file2.tmp *** diff --git a/src/cleanup/cleanup_milter.ref3 b/src/cleanup/cleanup_milter.ref3 new file mode 100644 index 0000000..656b561 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref3 @@ -0,0 +1,50 @@ +*** ENVELOPE RECORDS test-queue-file3.tmp *** + 0 message_size: 332 199 1 0 332 + 81 message_arrival_time: Sat Jan 20 20:53:54 2007 + 100 create_time: Sat Jan 20 20:53:59 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: me@porcupine.org + 180 pointer_record: 573 + 573 original_recipient: me@porcupine.org + 591 canceled_recipient: me@porcupine.org + 609 pointer_record: 1397 + 1397 original_recipient: em@porcupine.org + 1415 canceled_recipient: em@porcupine.org + 1433 pointer_record: 197 + 197 *** MESSAGE CONTENTS test-queue-file3.tmp *** + 199 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 261 regular_text: id B85F1290407; Sat, 20 Jan 2007 20:53:59 -0500 (EST) + 317 pointer_record: 842 + 842 pointer_record: 1315 + 1315 pointer_record: 1356 + 1356 pointer_record: 1450 + 1450 pointer_record: 1707 + 1707 pointer_record: 1748 + 1748 regular_text: From: me@porcupine.org + 1772 pointer_record: 1491 + 1491 pointer_record: 1789 + 1789 pointer_record: 1829 + 1829 regular_text: To: you@porcupine.org + 1852 pointer_record: 1531 + 1531 pointer_record: 1869 + 1869 pointer_record: 1948 + 1948 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 2010 pointer_record: 1610 + 1610 pointer_record: 2027 + 2027 pointer_record: 2089 + 2089 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 2134 pointer_record: 1672 + 1672 pointer_record: 2151 + 2151 pointer_record: 2186 + 2186 regular_text: Subject: hey! + 2201 padding: 0 + 2204 pointer_record: 489 + 489 pointer_record: 0 + 506 regular_text: + 508 regular_text: text + 514 pointer_record: 0 + 531 *** HEADER EXTRACTED test-queue-file3.tmp *** + 533 original_recipient: you@porcupine.org + 552 recipient: you@porcupine.org + 571 *** MESSAGE FILE END test-queue-file3.tmp *** diff --git a/src/cleanup/cleanup_milter.ref4 b/src/cleanup/cleanup_milter.ref4 new file mode 100644 index 0000000..4c3be60 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref4 @@ -0,0 +1,59 @@ +*** ENVELOPE RECORDS test-queue-file4.tmp *** + 0 message_size: 441 813 3 0 441 + 81 message_arrival_time: Sat Jan 20 19:52:41 2007 + 100 create_time: Sat Jan 20 19:52:47 2007 + 124 named_attribute: rewrite_context=local + 147 sender: wietse@porcupine.org + 169 named_attribute: log_client_name=hades.porcupine.org + 206 named_attribute: log_client_address=168.100.189.10 + 241 named_attribute: log_message_origin=hades.porcupine.org[168.100.189.10] + 297 named_attribute: log_helo_name=hades.porcupine.org + 332 named_attribute: log_protocol_name=SMTP + 356 named_attribute: client_name=hades.porcupine.org + 389 named_attribute: reverse_client_name=hades.porcupine.org + 430 named_attribute: client_address=168.100.189.10 + 461 named_attribute: helo_name=hades.porcupine.org + 492 named_attribute: client_address_type=2 + 515 named_attribute: dsn_orig_rcpt=rfc822;wietse@porcupine.org + 558 original_recipient: wietse@porcupine.org + 580 recipient: wietse@porcupine.org + 602 named_attribute: dsn_orig_rcpt=rfc822;alias@hades.porcupine.org + 650 original_recipient: alias@hades.porcupine.org + 677 recipient: wietse@porcupine.org + 699 named_attribute: dsn_orig_rcpt=rfc822;alias@hades.porcupine.org + 747 original_recipient: alias@hades.porcupine.org + 774 recipient: root@porcupine.org + 794 pointer_record: 1258 + 1258 original_recipient: 01 + 1262 canceled_recipient: 01 + 1266 pointer_record: 1283 + 1283 original_recipient: 02 + 1287 canceled_recipient: 02 + 1291 pointer_record: 1308 + 1308 original_recipient: 03 + 1312 canceled_recipient: 03 + 1316 pointer_record: 811 + 811 *** MESSAGE CONTENTS test-queue-file4.tmp *** + 813 regular_text: Received: from hades.porcupine.org (hades.porcupine.org [168.100.189.10]) + 888 regular_text: by hades.porcupine.org (Postfix) with SMTP id 38132290405; + 949 regular_text: Sat, 20 Jan 2007 19:52:41 -0500 (EST) + 989 regular_text: X: 1 + 995 padding: 0 + 1006 regular_text: 2 + 1010 regular_text: 3 + 1014 regular_text: 4 + 1018 regular_text: 5 + 1022 regular_text: 6 + 1026 regular_text: 7 + 1030 regular_text: Y: 1234567 + 1042 padding: 0 + 1047 regular_text: Message-Id: <20070121005247.38132290405@hades.porcupine.org> + 1109 regular_text: Date: Sat, 20 Jan 2007 19:52:41 -0500 (EST) + 1154 regular_text: From: wietse@porcupine.org + 1182 regular_text: To: undisclosed-recipients:; + 1212 pointer_record: 0 + 1229 regular_text: + 1231 regular_text: text + 1237 pointer_record: 0 + 1254 *** HEADER EXTRACTED test-queue-file4.tmp *** + 1256 *** MESSAGE FILE END test-queue-file4.tmp *** diff --git a/src/cleanup/cleanup_milter.ref5 b/src/cleanup/cleanup_milter.ref5 new file mode 100644 index 0000000..b5862a8 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref5 @@ -0,0 +1,29 @@ +*** ENVELOPE RECORDS test-queue-file5.tmp *** + 0 message_size: 376 237 1 0 376 + 81 message_arrival_time: Sun Jan 21 11:26:46 2007 + 100 create_time: Sun Jan 21 11:26:59 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: me@porcupine.org + 180 original_recipient: you@porcupine.org + 199 recipient: you@porcupine.org + 218 pointer_record: 0 + 235 *** MESSAGE CONTENTS test-queue-file5.tmp *** + 237 regular_text: Received: by hades.porcupine.org (Postfix, from userid 0) + 296 regular_text: id 38FA9290404; Sun, 21 Jan 2007 11:26:59 -0500 (EST) + 352 pointer_record: 707 + 707 regular_text: Subject: hya + 721 padding: 0 + 724 pointer_record: 662 + 662 regular_text: X: whatevershebringswesing + 690 pointer_record: 394 + 394 regular_text: Message-Id: <20070121162659.38FA9290404@hades.porcupine.org> + 456 regular_text: Date: Sun, 21 Jan 2007 11:26:46 -0500 (EST) + 501 regular_text: From: me@porcupine.org (Wietse Venema) + 541 regular_text: To: undisclosed-recipients:; + 571 pointer_record: 0 + 588 regular_text: + 590 regular_text: text + 596 pointer_record: 0 + 613 *** HEADER EXTRACTED test-queue-file5.tmp *** + 615 *** MESSAGE FILE END test-queue-file5.tmp *** diff --git a/src/cleanup/cleanup_milter.ref6a b/src/cleanup/cleanup_milter.ref6a new file mode 100644 index 0000000..193960d --- /dev/null +++ b/src/cleanup/cleanup_milter.ref6a @@ -0,0 +1,28 @@ +*** ENVELOPE RECORDS test-queue-file6.tmp *** + 0 message_size: 332 199 1 0 332 + 81 message_arrival_time: Sat Jan 20 20:53:54 2007 + 100 create_time: Sat Jan 20 20:53:59 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: me@porcupine.org + 180 pointer_record: 0 + 197 *** MESSAGE CONTENTS test-queue-file6.tmp *** + 199 pointer_record: 573 + 573 regular_text: X-Virus-Scanned: hya + 595 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 657 pointer_record: 261 + 261 regular_text: id B85F1290407; Sat, 20 Jan 2007 20:53:59 -0500 (EST) + 317 regular_text: From: me@porcupine.org + 341 regular_text: To: you@porcupine.org + 364 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 426 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 471 regular_text: Subject: hey! + 486 padding: 0 + 489 pointer_record: 0 + 506 regular_text: + 508 regular_text: text + 514 pointer_record: 0 + 531 *** HEADER EXTRACTED test-queue-file6.tmp *** + 533 original_recipient: you@porcupine.org + 552 recipient: you@porcupine.org + 571 *** MESSAGE FILE END test-queue-file6.tmp *** diff --git a/src/cleanup/cleanup_milter.ref6b b/src/cleanup/cleanup_milter.ref6b new file mode 100644 index 0000000..fdcb38e --- /dev/null +++ b/src/cleanup/cleanup_milter.ref6b @@ -0,0 +1,31 @@ +*** ENVELOPE RECORDS test-queue-file6.tmp *** + 0 message_size: 332 199 1 0 332 + 81 message_arrival_time: Sat Jan 20 20:53:54 2007 + 100 create_time: Sat Jan 20 20:53:59 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: me@porcupine.org + 180 pointer_record: 0 + 197 *** MESSAGE CONTENTS test-queue-file6.tmp *** + 199 pointer_record: 573 + 573 regular_text: X-Virus-Scanned: hya + 595 pointer_record: 674 + 674 regular_text: Domainkey-Signature: hya + 700 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 762 pointer_record: 657 + 657 pointer_record: 261 + 261 regular_text: id B85F1290407; Sat, 20 Jan 2007 20:53:59 -0500 (EST) + 317 regular_text: From: me@porcupine.org + 341 regular_text: To: you@porcupine.org + 364 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 426 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 471 regular_text: Subject: hey! + 486 padding: 0 + 489 pointer_record: 0 + 506 regular_text: + 508 regular_text: text + 514 pointer_record: 0 + 531 *** HEADER EXTRACTED test-queue-file6.tmp *** + 533 original_recipient: you@porcupine.org + 552 recipient: you@porcupine.org + 571 *** MESSAGE FILE END test-queue-file6.tmp *** diff --git a/src/cleanup/cleanup_milter.ref6c b/src/cleanup/cleanup_milter.ref6c new file mode 100644 index 0000000..9d58b6d --- /dev/null +++ b/src/cleanup/cleanup_milter.ref6c @@ -0,0 +1,33 @@ +*** ENVELOPE RECORDS test-queue-file6.tmp *** + 0 message_size: 332 199 1 0 332 + 81 message_arrival_time: Sat Jan 20 20:53:54 2007 + 100 create_time: Sat Jan 20 20:53:59 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: me@porcupine.org + 180 pointer_record: 0 + 197 *** MESSAGE CONTENTS test-queue-file6.tmp *** + 199 pointer_record: 573 + 573 regular_text: X-Virus-Scanned: hya + 595 pointer_record: 779 + 779 regular_text: DKIM-Signature: hya + 800 pointer_record: 674 + 674 regular_text: Domainkey-Signature: hya + 700 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 762 pointer_record: 657 + 657 pointer_record: 261 + 261 regular_text: id B85F1290407; Sat, 20 Jan 2007 20:53:59 -0500 (EST) + 317 regular_text: From: me@porcupine.org + 341 regular_text: To: you@porcupine.org + 364 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 426 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 471 regular_text: Subject: hey! + 486 padding: 0 + 489 pointer_record: 0 + 506 regular_text: + 508 regular_text: text + 514 pointer_record: 0 + 531 *** HEADER EXTRACTED test-queue-file6.tmp *** + 533 original_recipient: you@porcupine.org + 552 recipient: you@porcupine.org + 571 *** MESSAGE FILE END test-queue-file6.tmp *** diff --git a/src/cleanup/cleanup_milter.ref7 b/src/cleanup/cleanup_milter.ref7 new file mode 100644 index 0000000..ffc63a3 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref7 @@ -0,0 +1,32 @@ +*** ENVELOPE RECORDS test-queue-file7.tmp *** + 0 message_size: 332 199 1 0 332 + 81 message_arrival_time: Sat Jan 20 20:53:54 2007 + 100 create_time: Sat Jan 20 20:53:59 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: me@porcupine.org + 180 pointer_record: 0 + 197 *** MESSAGE CONTENTS test-queue-file7.tmp *** + 199 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 261 regular_text: id B85F1290407; Sat, 20 Jan 2007 20:53:59 -0500 (EST) + 317 pointer_record: 679 + 679 regular_text: DKIM-Signature: hya + 700 pointer_record: 636 + 636 regular_text: Domainkey-Signature: hya + 662 pointer_record: 573 + 573 regular_text: X-Virus-Scanned: hya + 595 regular_text: From: me@porcupine.org + 619 pointer_record: 341 + 341 regular_text: To: you@porcupine.org + 364 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 426 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 471 regular_text: Subject: hey! + 486 padding: 0 + 489 pointer_record: 0 + 506 regular_text: + 508 regular_text: text + 514 pointer_record: 0 + 531 *** HEADER EXTRACTED test-queue-file7.tmp *** + 533 original_recipient: you@porcupine.org + 552 recipient: you@porcupine.org + 571 *** MESSAGE FILE END test-queue-file7.tmp *** diff --git a/src/cleanup/cleanup_milter.ref8 b/src/cleanup/cleanup_milter.ref8 new file mode 100644 index 0000000..5aadfd4 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref8 @@ -0,0 +1,34 @@ +*** ENVELOPE RECORDS test-queue-file8.tmp *** + 0 message_size: 332 199 1 0 332 + 81 message_arrival_time: Sat Jan 20 20:53:54 2007 + 100 create_time: Sat Jan 20 20:53:59 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: me@porcupine.org + 180 pointer_record: 0 + 197 *** MESSAGE CONTENTS test-queue-file8.tmp *** + 199 pointer_record: 573 + 573 regular_text: inserted-at-1: hya + 593 pointer_record: 672 + 672 regular_text: inserted-at-2: hya + 692 pointer_record: 771 + 771 regular_text: inserted-at-3: hya + 791 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 853 pointer_record: 754 + 754 pointer_record: 655 + 655 pointer_record: 261 + 261 regular_text: id B85F1290407; Sat, 20 Jan 2007 20:53:59 -0500 (EST) + 317 regular_text: From: me@porcupine.org + 341 regular_text: To: you@porcupine.org + 364 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 426 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 471 regular_text: Subject: hey! + 486 padding: 0 + 489 pointer_record: 0 + 506 regular_text: + 508 regular_text: text + 514 pointer_record: 0 + 531 *** HEADER EXTRACTED test-queue-file8.tmp *** + 533 original_recipient: you@porcupine.org + 552 recipient: you@porcupine.org + 571 *** MESSAGE FILE END test-queue-file8.tmp *** diff --git a/src/cleanup/cleanup_milter.ref9 b/src/cleanup/cleanup_milter.ref9 new file mode 100644 index 0000000..9cfd626 --- /dev/null +++ b/src/cleanup/cleanup_milter.ref9 @@ -0,0 +1,33 @@ +*** ENVELOPE RECORDS test-queue-file9.tmp *** + 0 message_size: 332 199 1 0 332 + 81 message_arrival_time: Sat Jan 20 20:53:54 2007 + 100 create_time: Sat Jan 20 20:53:59 2007 + 124 named_attribute: rewrite_context=local + 147 sender_fullname: Wietse Venema + 162 sender: me@porcupine.org + 180 pointer_record: 0 + 197 *** MESSAGE CONTENTS test-queue-file9.tmp *** + 199 pointer_record: 573 + 573 regular_text: inserted-at-1: hya + 593 regular_text: Received: by hades.porcupine.org (Postfix, from userid 1001) + 655 pointer_record: 261 + 261 regular_text: id B85F1290407; Sat, 20 Jan 2007 20:53:59 -0500 (EST) + 317 pointer_record: 672 + 672 regular_text: inserted-at-3: hya + 692 regular_text: From: me@porcupine.org + 716 pointer_record: 733 + 733 regular_text: inserted-at-5: hya + 753 pointer_record: 341 + 341 regular_text: To: you@porcupine.org + 364 regular_text: Message-Id: <20060725192735.5EC2D29013F@hades.porcupine.org> + 426 regular_text: Date: Tue, 25 Jul 2006 15:27:19 -0400 (EDT) + 471 regular_text: Subject: hey! + 486 padding: 0 + 489 pointer_record: 0 + 506 regular_text: + 508 regular_text: text + 514 pointer_record: 0 + 531 *** HEADER EXTRACTED test-queue-file9.tmp *** + 533 original_recipient: you@porcupine.org + 552 recipient: you@porcupine.org + 571 *** MESSAGE FILE END test-queue-file9.tmp *** diff --git a/src/cleanup/cleanup_milter.reg14a b/src/cleanup/cleanup_milter.reg14a new file mode 100644 index 0000000..1a9ccf1 --- /dev/null +++ b/src/cleanup/cleanup_milter.reg14a @@ -0,0 +1 @@ +/./ reject diff --git a/src/cleanup/cleanup_milter.reg14b b/src/cleanup/cleanup_milter.reg14b new file mode 100644 index 0000000..332ba69 --- /dev/null +++ b/src/cleanup/cleanup_milter.reg14b @@ -0,0 +1 @@ +/./ FILTER transport:nexthop:port diff --git a/src/cleanup/cleanup_milter.reg14c b/src/cleanup/cleanup_milter.reg14c new file mode 100644 index 0000000..0421976 --- /dev/null +++ b/src/cleanup/cleanup_milter.reg14c @@ -0,0 +1 @@ +/./ REDIRECT foo@examle.com diff --git a/src/cleanup/cleanup_milter.reg14d b/src/cleanup/cleanup_milter.reg14d new file mode 100644 index 0000000..78647a3 --- /dev/null +++ b/src/cleanup/cleanup_milter.reg14d @@ -0,0 +1 @@ +/./ DISCARD diff --git a/src/cleanup/cleanup_milter.reg14e b/src/cleanup/cleanup_milter.reg14e new file mode 100644 index 0000000..c88ee2c --- /dev/null +++ b/src/cleanup/cleanup_milter.reg14e @@ -0,0 +1 @@ +/./ HOLD diff --git a/src/cleanup/cleanup_milter.reg14f b/src/cleanup/cleanup_milter.reg14f new file mode 100644 index 0000000..1a9ccf1 --- /dev/null +++ b/src/cleanup/cleanup_milter.reg14f @@ -0,0 +1 @@ +/./ reject diff --git a/src/cleanup/cleanup_milter.reg14g b/src/cleanup/cleanup_milter.reg14g new file mode 100644 index 0000000..1a9ccf1 --- /dev/null +++ b/src/cleanup/cleanup_milter.reg14g @@ -0,0 +1 @@ +/./ reject diff --git a/src/cleanup/cleanup_milter.reg15a b/src/cleanup/cleanup_milter.reg15a new file mode 100644 index 0000000..1a9ccf1 --- /dev/null +++ b/src/cleanup/cleanup_milter.reg15a @@ -0,0 +1 @@ +/./ reject diff --git a/src/cleanup/cleanup_milter.reg15b b/src/cleanup/cleanup_milter.reg15b new file mode 100644 index 0000000..332ba69 --- /dev/null +++ b/src/cleanup/cleanup_milter.reg15b @@ -0,0 +1 @@ +/./ FILTER transport:nexthop:port diff --git a/src/cleanup/cleanup_milter.reg15c b/src/cleanup/cleanup_milter.reg15c new file mode 100644 index 0000000..0421976 --- /dev/null +++ b/src/cleanup/cleanup_milter.reg15c @@ -0,0 +1 @@ +/./ REDIRECT foo@examle.com diff --git a/src/cleanup/cleanup_milter.reg15d b/src/cleanup/cleanup_milter.reg15d new file mode 100644 index 0000000..78647a3 --- /dev/null +++ b/src/cleanup/cleanup_milter.reg15d @@ -0,0 +1 @@ +/./ DISCARD diff --git a/src/cleanup/cleanup_milter.reg15e b/src/cleanup/cleanup_milter.reg15e new file mode 100644 index 0000000..c88ee2c --- /dev/null +++ b/src/cleanup/cleanup_milter.reg15e @@ -0,0 +1 @@ +/./ HOLD diff --git a/src/cleanup/cleanup_milter.reg15f b/src/cleanup/cleanup_milter.reg15f new file mode 100644 index 0000000..a629bf2 --- /dev/null +++ b/src/cleanup/cleanup_milter.reg15f @@ -0,0 +1 @@ +/./ redirect x@y.z diff --git a/src/cleanup/cleanup_milter.reg15g b/src/cleanup/cleanup_milter.reg15g new file mode 100644 index 0000000..0a25be3 --- /dev/null +++ b/src/cleanup/cleanup_milter.reg15g @@ -0,0 +1 @@ +/./ filter x:y:z diff --git a/src/cleanup/cleanup_milter.reg15h b/src/cleanup/cleanup_milter.reg15h new file mode 100644 index 0000000..8c97639 --- /dev/null +++ b/src/cleanup/cleanup_milter.reg15h @@ -0,0 +1,2 @@ +/YES/ reject whatever +/NO/ filter x:y:z diff --git a/src/cleanup/cleanup_milter.reg15i b/src/cleanup/cleanup_milter.reg15i new file mode 100644 index 0000000..8c97639 --- /dev/null +++ b/src/cleanup/cleanup_milter.reg15i @@ -0,0 +1,2 @@ +/YES/ reject whatever +/NO/ filter x:y:z diff --git a/src/cleanup/cleanup_milter.reg16a b/src/cleanup/cleanup_milter.reg16a new file mode 100644 index 0000000..674aa5d --- /dev/null +++ b/src/cleanup/cleanup_milter.reg16a @@ -0,0 +1,2 @@ +/YES/ bcc foo@example.com +/NO/ bcc bar@example.com diff --git a/src/cleanup/cleanup_out.c b/src/cleanup/cleanup_out.c new file mode 100644 index 0000000..8905fad --- /dev/null +++ b/src/cleanup/cleanup_out.c @@ -0,0 +1,225 @@ +/*++ +/* NAME +/* cleanup_out 3 +/* SUMMARY +/* record output support +/* SYNOPSIS +/* #include "cleanup.h" +/* +/* int CLEANUP_OUT_OK(state) +/* CLEANUP_STATE *state; +/* +/* void cleanup_out(state, type, data, len) +/* CLEANUP_STATE *state; +/* int type; +/* const char *data; +/* ssize_t len; +/* +/* void cleanup_out_string(state, type, str) +/* CLEANUP_STATE *state; +/* int type; +/* const char *str; +/* +/* void CLEANUP_OUT_BUF(state, type, buf) +/* CLEANUP_STATE *state; +/* int type; +/* VSTRING *buf; +/* +/* void cleanup_out_format(state, type, format, ...) +/* CLEANUP_STATE *state; +/* int type; +/* const char *format; +/* +/* void cleanup_out_header(state, buf) +/* CLEANUP_STATE *state; +/* VSTRING *buf; +/* DESCRIPTION +/* This module writes records to the output stream. +/* +/* CLEANUP_OUT_OK() is a macro that evaluates to non-zero +/* as long as it makes sense to produce output. All output +/* routines below check for this condition. +/* +/* cleanup_out() is the main record output routine. It writes +/* one record of the specified type, with the specified data +/* and length to the output stream. +/* +/* cleanup_out_string() outputs one string as a record. +/* +/* CLEANUP_OUT_BUF() is an unsafe macro that outputs +/* one string buffer as a record. +/* +/* cleanup_out_format() formats its arguments and writes +/* the result as a record. +/* +/* cleanup_out_header() outputs a multi-line header as records +/* of the specified type. The input is expected to be newline +/* separated (not newline terminated), and is modified. +/* 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 <errno.h> +#include <stdlib.h> /* 44BSD stdarg.h uses abort() */ +#include <stdarg.h> +#include <string.h> + +/* Utility library. */ + +#include <msg.h> +#include <vstring.h> +#include <vstream.h> +#include <split_at.h> +#include <stringops.h> + +/* Global library. */ + +#include <record.h> +#include <rec_type.h> +#include <cleanup_user.h> +#include <mail_params.h> +#include <lex_822.h> +#include <smtputf8.h> + +/* Application-specific. */ + +#include "cleanup.h" + +#define STR vstring_str + +/* cleanup_out - output one single record */ + +void cleanup_out(CLEANUP_STATE *state, int type, const char *string, ssize_t len) +{ + int err = 0; + + /* + * Long message header lines have to be read and written as multiple + * records. Other header/body content, and envelope data, is copied one + * record at a time. Be sure to not skip a zero-length request. + * + * XXX We don't know if we're writing a message header or not, but that is + * not a problem. A REC_TYPE_NORM or REC_TYPE_CONT record can always be + * chopped up into an equivalent set of REC_TYPE_CONT plus REC_TYPE_NORM + * records. + */ + if (CLEANUP_OUT_OK(state) == 0) + return; + +#define TEXT_RECORD(t) ((t) == REC_TYPE_NORM || (t) == REC_TYPE_CONT) + + if (var_line_limit <= 0) + msg_panic("cleanup_out: bad line length limit: %d", var_line_limit); + do { + if (len > var_line_limit && TEXT_RECORD(type)) { + err = rec_put(state->dst, REC_TYPE_CONT, string, var_line_limit); + string += var_line_limit; + len -= var_line_limit; + } else { + err = rec_put(state->dst, type, string, len); + break; + } + } while (len > 0 && err >= 0); + + if (err < 0) { + if (errno == EFBIG) { + msg_warn("%s: queue file size limit exceeded", + state->queue_id); + state->errs |= CLEANUP_STAT_SIZE; + } else { + msg_warn("%s: write queue file: %m", state->queue_id); + state->errs |= CLEANUP_STAT_WRITE; + } + } +} + +/* cleanup_out_string - output string to one single record */ + +void cleanup_out_string(CLEANUP_STATE *state, int type, const char *string) +{ + cleanup_out(state, type, string, strlen(string)); +} + +/* cleanup_out_format - output one formatted record */ + +void cleanup_out_format(CLEANUP_STATE *state, int type, const char *fmt,...) +{ + static VSTRING *vp; + va_list ap; + + if (vp == 0) + vp = vstring_alloc(100); + va_start(ap, fmt); + vstring_vsprintf(vp, fmt, ap); + va_end(ap); + CLEANUP_OUT_BUF(state, type, vp); +} + +/* cleanup_out_header - output one multi-line header as a bunch of records */ + +void cleanup_out_header(CLEANUP_STATE *state, VSTRING *header_buf) +{ + char *start = vstring_str(header_buf); + char *line; + char *next_line; + ssize_t line_len; + + /* + * Fix 20140711: Auto-detect the presence of a non-ASCII header. + */ + if (var_smtputf8_enable && *STR(header_buf) && !allascii(STR(header_buf))) { + state->smtputf8 |= SMTPUTF8_FLAG_HEADER; + /* Fix 20140713: request SMTPUTF8 support selectively. */ + if (state->flags & CLEANUP_FLAG_AUTOUTF8) + state->smtputf8 |= SMTPUTF8_FLAG_REQUESTED; + } + + /* + * Prepend a tab to continued header lines that went through the address + * rewriting machinery. See cleanup_fold_header(state) below for the form + * of such header lines. NB: This code destroys the header. We could try + * to avoid clobbering it, but we're not going to use the data any + * further. + * + * XXX We prefer to truncate a header at the last line boundary before the + * header size limit. If this would undershoot the limit by more than + * 10%, we truncate between line boundaries to avoid losing too much + * text. This "unkind cut" may result in syntax errors and may trigger + * warnings from down-stream MTAs. + * + * If Milter is enabled, pad a short header record with a dummy record so + * that a header record can safely be overwritten by a pointer record. + * This simplifies header modification enormously. + */ + for (line = start; line; line = next_line) { + next_line = split_at(line, '\n'); + line_len = next_line ? next_line - 1 - line : strlen(line); + if (line + line_len > start + var_header_limit) { + if (line - start > 0.9 * var_header_limit) /* nice cut */ + break; + start[var_header_limit] = 0; /* unkind cut */ + next_line = 0; + } + if (line == start) { + cleanup_out_string(state, REC_TYPE_NORM, line); + if ((state->milters || cleanup_milters) + && line_len < REC_TYPE_PTR_PAYL_SIZE) + rec_pad(state->dst, REC_TYPE_DTXT, + REC_TYPE_PTR_PAYL_SIZE - line_len); + } else if (IS_SPACE_TAB(*line)) { + cleanup_out_string(state, REC_TYPE_NORM, line); + } else { + cleanup_out_format(state, REC_TYPE_NORM, "\t%s", line); + } + } +} diff --git a/src/cleanup/cleanup_out_recipient.c b/src/cleanup/cleanup_out_recipient.c new file mode 100644 index 0000000..40735e2 --- /dev/null +++ b/src/cleanup/cleanup_out_recipient.c @@ -0,0 +1,265 @@ +/*++ +/* NAME +/* cleanup_out_recipient 3 +/* SUMMARY +/* envelope recipient output filter +/* SYNOPSIS +/* #include "cleanup.h" +/* +/* void cleanup_out_recipient(state, dsn_orig_recipient, +/* dsn_notify, orig_recipient, +/* recipient) +/* CLEANUP_STATE *state; +/* const char *dsn_orig_recipient; +/* const char *dsn_notify; +/* const char *orig_recipient; +/* const char *recipient; +/* DESCRIPTION +/* This module implements an envelope recipient output filter. +/* +/* cleanup_out_recipient() performs virtual table expansion +/* and recipient duplicate filtering, and appends the +/* resulting recipients to the output stream. It also +/* generates DSN SUCCESS notifications. +/* +/* Arguments: +/* .IP state +/* Cleanup server state. +/* .IP dsn_orig_recipient +/* DSN original recipient information. +/* .IP dsn_notify +/* DSN notify flags. +/* .IP orig_recipient +/* Envelope recipient as received by Postfix. +/* .IP recipient +/* Envelope recipient as rewritten by Postfix. +/* CONFIGURATION +/* .ad +/* .fi +/* .IP enable_original_recipient +/* Enable orig_recipient support. +/* .IP local_duplicate_filter_limit +/* Upper bound to the size of the recipient duplicate filter. +/* Zero means no limit; this may cause the mail system to +/* become stuck. +/* .IP virtual_alias_maps +/* list of virtual address lookup tables. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <string.h> + +/* Utility library. */ + +#include <argv.h> +#include <msg.h> + +/* Global library. */ + +#include <been_here.h> +#include <mail_params.h> +#include <rec_type.h> +#include <ext_prop.h> +#include <cleanup_user.h> +#include <dsn_mask.h> +#include <recipient_list.h> +#include <dsn.h> +#include <trace.h> +#include <verify.h> +#include <mail_queue.h> /* cleanup_trace_path */ +#include <mail_proto.h> +#include <msg_stats.h> + +/* Application-specific. */ + +#include "cleanup.h" + +/* cleanup_trace_append - update trace logfile */ + +static void cleanup_trace_append(CLEANUP_STATE *state, RECIPIENT *rcpt, + DSN *dsn) +{ + MSG_STATS stats; + + if (cleanup_trace_path == 0) { + cleanup_trace_path = vstring_alloc(10); + mail_queue_path(cleanup_trace_path, MAIL_QUEUE_TRACE, + state->queue_id); + } + if (trace_append(BOUNCE_FLAG_CLEAN, state->queue_id, + CLEANUP_MSG_STATS(&stats, state), + rcpt, "none", dsn) != 0) { + msg_warn("%s: trace logfile update error", state->queue_id); + state->errs |= CLEANUP_STAT_WRITE; + } +} + +/* cleanup_verify_append - update verify daemon */ + +static void cleanup_verify_append(CLEANUP_STATE *state, RECIPIENT *rcpt, + DSN *dsn, int verify_status) +{ + MSG_STATS stats; + + if (verify_append(state->queue_id, CLEANUP_MSG_STATS(&stats, state), + rcpt, "none", dsn, verify_status) != 0) { + msg_warn("%s: verify service update error", state->queue_id); + state->errs |= CLEANUP_STAT_WRITE; + } +} + +/* cleanup_out_recipient - envelope recipient output filter */ + +void cleanup_out_recipient(CLEANUP_STATE *state, + const char *dsn_orcpt, + int dsn_notify, + const char *orcpt, + const char *recip) +{ + ARGV *argv; + char **cpp; + + /* + * XXX Not elegant, but eliminates complexity in the record reading loop. + */ + if (dsn_orcpt == 0) + dsn_orcpt = ""; + + /* + * Distinguish between different original recipient addresses that map + * onto the same mailbox. The recipient will use our original recipient + * message header to figure things out. + * + * Postfix 2.2 compatibility: when ignoring differences in Postfix original + * recipient information, also ignore differences in DSN attributes. We + * do, however, keep the DSN attributes of the recipient that survives + * duplicate elimination. + */ +#define STREQ(x, y) (strcmp((x), (y)) == 0) + + if ((state->flags & CLEANUP_FLAG_MAP_OK) == 0 + || cleanup_virt_alias_maps == 0) { + if ((var_enable_orcpt ? + been_here(state->dups, "%s\n%d\n%s\n%s", + dsn_orcpt, dsn_notify, orcpt, recip) : + been_here_fixed(state->dups, recip)) == 0) { + if (dsn_notify) + cleanup_out_format(state, REC_TYPE_ATTR, "%s=%d", + MAIL_ATTR_DSN_NOTIFY, dsn_notify); + if (*dsn_orcpt) + cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s", + MAIL_ATTR_DSN_ORCPT, dsn_orcpt); + cleanup_out_string(state, REC_TYPE_ORCP, orcpt); + cleanup_out_string(state, REC_TYPE_RCPT, recip); + state->rcpt_count++; + } + } + + /* + * XXX DSN. RFC 3461 gives us three options for multi-recipient aliases + * (we're treating single recipient aliases as a special case of + * multi-recipient aliases, one argument being that it is none of the + * sender's business). + * + * (a) Don't propagate ENVID, NOTIFY, RET, or ORCPT. If NOTIFY specified + * SUCCESS, send a "relayed" DSN. + * + * (b) Propagate ENVID, (NOTIFY minus SUCCESS), RET, and ORCPT. If NOTIFY + * specified SUCCESS, send an "expanded" DSN. + * + * (c) Propagate ENVID, NOTIFY, RET, and ORCPT to one recipient only. Send + * no DSN. + * + * In all three cases we are modifying at least one NOTIFY value. Either we + * have to record explicit dsn_notify records, or we must not allow the + * use of a per-message non-default NOTIFY value that applies to all + * recipient records. + * + * Alternatives (a) and (c) require that we store explicit per-recipient RET + * and ENVID records, at least for the recipients that are excluded from + * RET and ENVID propagation. This means storing explicit ENVID records + * to indicate that the information does not exist. All this makes + * alternative (b) more and more attractive. It is no surprise that we + * use (b) here and in the local delivery agent. + * + * In order to generate a SUCCESS notification from the cleanup server we + * have to write the trace logfile record now. We're NOT going to flush + * the trace file from the cleanup server; if we need to write bounce + * logfile records, and the bounce service fails, we must be able to + * cancel the entire cleanup request including any success or failure + * notifications. The queue manager will flush the trace (and bounce) + * logfile, possibly after it has generated its own success or failure + * notification records. + * + * Postfix 2.2 compatibility: when ignoring differences in Postfix original + * recipient information, also ignore differences in DSN attributes. We + * do, however, keep the DSN attributes of the recipient that survives + * duplicate elimination. + * + * In the case of a verify(8) request for a one-to-many alias, declare the + * alias address as "deliverable". Do not verify the individual addresses + * in the expansion because that results in multiple verify(8) updates + * for one verify(8) request. + * + * Multiple verify(8) updates for one verify(8) request would overwrite + * each other's status, and if the last status update is "undeliverable", + * then the whole alias is flagged as undeliverable. + */ + else { + RECIPIENT rcpt; + DSN dsn; + + argv = cleanup_map1n_internal(state, recip, cleanup_virt_alias_maps, + cleanup_ext_prop_mask & EXT_PROP_VIRTUAL); + if (argv->argc > 1 && (state->tflags & DEL_REQ_FLAG_MTA_VRFY)) { + (void) DSN_SIMPLE(&dsn, "2.0.0", "aliased to multiple recipients"); + dsn.action = "deliverable"; + RECIPIENT_ASSIGN(&rcpt, 0, dsn_orcpt, dsn_notify, orcpt, recip); + cleanup_verify_append(state, &rcpt, &dsn, DEL_RCPT_STAT_OK); + argv_free(argv); + return; + } + if ((dsn_notify & DSN_NOTIFY_SUCCESS) + && (argv->argc > 1 || strcmp(recip, argv->argv[0]) != 0)) { + (void) DSN_SIMPLE(&dsn, "2.0.0", "alias expanded"); + dsn.action = "expanded"; + RECIPIENT_ASSIGN(&rcpt, 0, dsn_orcpt, dsn_notify, orcpt, recip); + cleanup_trace_append(state, &rcpt, &dsn); + dsn_notify = (dsn_notify == DSN_NOTIFY_SUCCESS ? DSN_NOTIFY_NEVER : + dsn_notify & ~DSN_NOTIFY_SUCCESS); + } + for (cpp = argv->argv; *cpp; cpp++) { + if ((var_enable_orcpt ? + been_here(state->dups, "%s\n%d\n%s\n%s", + dsn_orcpt, dsn_notify, orcpt, *cpp) : + been_here_fixed(state->dups, *cpp)) == 0) { + if (dsn_notify) + cleanup_out_format(state, REC_TYPE_ATTR, "%s=%d", + MAIL_ATTR_DSN_NOTIFY, dsn_notify); + if (*dsn_orcpt) + cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s", + MAIL_ATTR_DSN_ORCPT, dsn_orcpt); + cleanup_out_string(state, REC_TYPE_ORCP, orcpt); + cleanup_out_string(state, REC_TYPE_RCPT, *cpp); + state->rcpt_count++; + } + } + argv_free(argv); + } +} diff --git a/src/cleanup/cleanup_region.c b/src/cleanup/cleanup_region.c new file mode 100644 index 0000000..b2acbee --- /dev/null +++ b/src/cleanup/cleanup_region.c @@ -0,0 +1,232 @@ +/*++ +/* NAME +/* cleanup_region 3 +/* SUMMARY +/* queue file region manager +/* SYNOPSIS +/* #include "cleanup.h" +/* +/* void cleanup_region_init(state) +/* CLEANUP_STATE *state; +/* +/* CLEANUP_REGION *cleanup_region_open(state, space_needed) +/* CLEANUP_STATE *state; +/* ssize_t space_needed; +/* +/* int cleanup_region_close(state, rp) +/* CLEANUP_STATE *state; +/* CLEANUP_REGION *rp; +/* +/* CLEANUP_REGION *cleanup_region_return(state, rp) +/* CLEANUP_STATE *state; +/* CLEANUP_REGION *rp; +/* +/* void cleanup_region_done(state) +/* CLEANUP_STATE *state; +/* DESCRIPTION +/* This module maintains queue file regions. Regions are created +/* on-the-fly and can be reused multiple times. Each region +/* structure consists of a file offset, a length (0 for an +/* open-ended region at the end of the file), a write offset +/* (maintained by the caller), and list linkage. Region +/* boundaries are not enforced by this module. It is up to the +/* caller to ensure that they stay within bounds. +/* +/* cleanup_region_init() performs mandatory initialization and +/* overlays an initial region structure over an already existing +/* queue file. This function must not be called before the +/* queue file is complete. +/* +/* cleanup_region_open() opens an existing region or creates +/* a new region that can accommodate at least the specified +/* amount of space. A new region is an open-ended region at +/* the end of the file; it must be closed (see next) before +/* unrelated data can be appended to the same file. +/* +/* cleanup_region_close() indicates that a region will not be +/* updated further. With an open-ended region, the region's +/* end is frozen just before the caller-maintained write offset. +/* With a close-ended region, unused space (beginning at the +/* caller-maintained write offset) may be returned to the free +/* pool. +/* +/* cleanup_region_return() returns a list of regions to the +/* free pool, and returns a null pointer. To avoid fragmentation, +/* adjacent free regions may be coalesced together. +/* +/* cleanup_region_done() destroys all in-memory information +/* that was allocated for administering queue file regions. +/* +/* Arguments: +/* .IP state +/* Queue file and message processing state. This state is +/* updated as records are processed and as errors happen. +/* .IP space_needed +/* The minimum region size needed. +/* 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> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <warn_stat.h> + +/* Application-specific. */ + +#include <cleanup.h> + +/* cleanup_region_alloc - create queue file region */ + +static CLEANUP_REGION *cleanup_region_alloc(off_t start, off_t len) +{ + CLEANUP_REGION *rp; + + rp = (CLEANUP_REGION *) mymalloc(sizeof(*rp)); + rp->write_offs = rp->start = start; + rp->len = len; + rp->next = 0; + + return (rp); +} + +/* cleanup_region_free - destroy region list */ + +static CLEANUP_REGION *cleanup_region_free(CLEANUP_REGION *regions) +{ + CLEANUP_REGION *rp; + CLEANUP_REGION *next; + + for (rp = regions; rp != 0; rp = next) { + next = rp->next; + myfree((void *) rp); + } + return (0); +} + +/* cleanup_region_init - create initial region overlay */ + +void cleanup_region_init(CLEANUP_STATE *state) +{ + const char *myname = "cleanup_region_init"; + + /* + * Sanity check. + */ + if (state->free_regions != 0 || state->body_regions != 0) + msg_panic("%s: repeated call", myname); + + /* + * Craft the first regions on the fly, from circumstantial evidence. + */ + state->body_regions = + cleanup_region_alloc(state->append_hdr_pt_target, + state->xtra_offset - state->append_hdr_pt_target); + if (msg_verbose) + msg_info("%s: body start %ld len %ld", + myname, (long) state->body_regions->start, (long) state->body_regions->len); +} + +/* cleanup_region_open - open existing region or create new region */ + +CLEANUP_REGION *cleanup_region_open(CLEANUP_STATE *state, ssize_t len) +{ + const char *myname = "cleanup_region_open"; + CLEANUP_REGION **rpp; + CLEANUP_REGION *rp; + struct stat st; + + /* + * Find the first region that is large enough, or create a new region. + */ + for (rpp = &state->free_regions; /* see below */ ; rpp = &(rp->next)) { + + /* + * Create an open-ended region at the end of the queue file. We + * freeze the region size after we stop writing to it. XXX Assume + * that fstat() returns a file size that is never less than the file + * append offset. It is not a problem if fstat() returns a larger + * result; we would just waste some space. + */ + if ((rp = *rpp) == 0) { + if (fstat(vstream_fileno(state->dst), &st) < 0) + msg_fatal("%s: fstat file %s: %m", myname, cleanup_path); + rp = cleanup_region_alloc(st.st_size, 0); + break; + } + + /* + * Reuse an existing region. + */ + if (rp->len >= len) { + (*rpp) = rp->next; + rp->next = 0; + rp->write_offs = rp->start; + break; + } + + /* + * Skip a too small region. + */ + if (msg_verbose) + msg_info("%s: skip start %ld len %ld < %ld", + myname, (long) rp->start, (long) rp->len, (long) len); + } + if (msg_verbose) + msg_info("%s: done start %ld len %ld", + myname, (long) rp->start, (long) rp->len); + return (rp); +} + +/* cleanup_region_close - freeze queue file region size */ + +void cleanup_region_close(CLEANUP_STATE *unused_state, CLEANUP_REGION *rp) +{ + const char *myname = "cleanup_region_close"; + + /* + * If this region is still open ended, freeze the size. If this region is + * closed, some future version of this routine may shrink the size and + * return the unused portion to the free pool. + */ + if (rp->len == 0) + rp->len = rp->write_offs - rp->start; + if (msg_verbose) + msg_info("%s: freeze start %ld len %ld", + myname, (long) rp->start, (long) rp->len); +} + +/* cleanup_region_return - return region list to free pool */ + +CLEANUP_REGION *cleanup_region_return(CLEANUP_STATE *state, CLEANUP_REGION *rp) +{ + CLEANUP_REGION **rpp; + + for (rpp = &state->free_regions; (*rpp) != 0; rpp = &(*rpp)->next) + /* void */ ; + *rpp = rp; + return (0); +} + +/* cleanup_region_done - destroy region metadata */ + +void cleanup_region_done(CLEANUP_STATE *state) +{ + if (state->free_regions != 0) + state->free_regions = cleanup_region_free(state->free_regions); + if (state->body_regions != 0) + state->body_regions = cleanup_region_free(state->body_regions); +} diff --git a/src/cleanup/cleanup_rewrite.c b/src/cleanup/cleanup_rewrite.c new file mode 100644 index 0000000..3c81e7b --- /dev/null +++ b/src/cleanup/cleanup_rewrite.c @@ -0,0 +1,123 @@ +/*++ +/* NAME +/* cleanup_rewrite 3 +/* SUMMARY +/* address canonicalization +/* SYNOPSIS +/* #include <cleanup.h> +/* +/* int cleanup_rewrite_external(context_name, result, addr) +/* const char *context; +/* VSTRING *result; +/* const char *addr; +/* +/* int cleanup_rewrite_internal(context_name, result, addr) +/* const char *context; +/* VSTRING *result; +/* const char *addr; +/* +/* int cleanup_rewrite_tree(context_name, tree) +/* const char *context; +/* TOK822 *tree; +/* DESCRIPTION +/* This module rewrites addresses to canonical form, adding missing +/* domains and stripping source routes etc., and performs +/* \fIcanonical\fR map lookups to map addresses to official form. +/* These functions return non-zero when the address was changed. +/* +/* cleanup_rewrite_init() performs one-time initialization. +/* +/* cleanup_rewrite_external() rewrites the external (quoted) string +/* form of an address. +/* +/* cleanup_rewrite_internal() is a wrapper around the +/* cleanup_rewrite_external() routine that transforms from +/* internal (quoted) string form to external form and back. +/* +/* cleanup_rewrite_tree() is a wrapper around the +/* cleanup_rewrite_external() routine that transforms from +/* internal parse tree form to external form and back. +/* +/* Arguments: +/* .IP context_name +/* The name of an address rewriting context that supplies +/* the equivalents of myorigin and mydomain. +/* .IP result +/* Result buffer. +/* .IP addr +/* Input buffer. +/* DIAGNOSTICS +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> + +/* Utility library. */ + +#include <msg.h> +#include <vstring.h> + +/* Global library. */ + +#include <tok822.h> +#include <rewrite_clnt.h> +#include <quote_822_local.h> + +/* Application-specific. */ + +#include "cleanup.h" + +#define STR vstring_str + +/* cleanup_rewrite_external - rewrite address external form */ + +int cleanup_rewrite_external(const char *context_name, VSTRING *result, + const char *addr) +{ + rewrite_clnt(context_name, addr, result); + return (strcmp(STR(result), addr) != 0); +} + +/* cleanup_rewrite_tree - rewrite address node */ + +int cleanup_rewrite_tree(const char *context_name, TOK822 *tree) +{ + VSTRING *dst = vstring_alloc(100); + VSTRING *src = vstring_alloc(100); + int did_rewrite; + + tok822_externalize(src, tree->head, TOK822_STR_DEFL); + did_rewrite = cleanup_rewrite_external(context_name, dst, STR(src)); + tok822_free_tree(tree->head); + tree->head = tok822_scan(STR(dst), &tree->tail); + vstring_free(dst); + vstring_free(src); + return (did_rewrite); +} + +/* cleanup_rewrite_internal - rewrite address internal form */ + +int cleanup_rewrite_internal(const char *context_name, + VSTRING *result, const char *addr) +{ + VSTRING *dst = vstring_alloc(100); + VSTRING *src = vstring_alloc(100); + int did_rewrite; + + quote_822_local(src, addr); + did_rewrite = cleanup_rewrite_external(context_name, dst, STR(src)); + unquote_822_local(result, STR(dst)); + vstring_free(dst); + vstring_free(src); + return (did_rewrite); +} diff --git a/src/cleanup/cleanup_state.c b/src/cleanup/cleanup_state.c new file mode 100644 index 0000000..99adf84 --- /dev/null +++ b/src/cleanup/cleanup_state.c @@ -0,0 +1,200 @@ +/*++ +/* NAME +/* cleanup_state 3 +/* SUMMARY +/* per-message state variables +/* SYNOPSIS +/* #include "cleanup.h" +/* +/* CLEANUP_STATE *cleanup_state_alloc(src) +/* VSTREAM *src; +/* +/* void cleanup_state_free(state) +/* CLEANUP_STATE *state; +/* DESCRIPTION +/* This module maintains about two dozen state variables +/* that are used by many routines in the course of processing one +/* message. +/* +/* cleanup_state_alloc() initializes the per-message state variables. +/* +/* cleanup_state_free() cleans up. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> + +/* Utility library. */ + +#include <mymalloc.h> +#include <vstring.h> +#include <htable.h> + +/* Global library. */ + +#include <been_here.h> +#include <mail_params.h> +#include <mime_state.h> +#include <mail_proto.h> + +/* Milter library. */ + +#include <milter.h> + +/* Application-specific. */ + +#include "cleanup.h" + +/* cleanup_state_alloc - initialize global state */ + +CLEANUP_STATE *cleanup_state_alloc(VSTREAM *src) +{ + CLEANUP_STATE *state = (CLEANUP_STATE *) mymalloc(sizeof(*state)); + + state->attr_buf = vstring_alloc(10); + state->temp1 = vstring_alloc(10); + state->temp2 = vstring_alloc(10); + if (cleanup_strip_chars) + state->stripped_buf = vstring_alloc(10); + state->src = src; + state->dst = 0; + state->handle = 0; + state->queue_name = 0; + state->queue_id = 0; + state->arrival_time.tv_sec = state->arrival_time.tv_usec = 0; + state->fullname = 0; + state->sender = 0; + state->recip = 0; + state->orig_rcpt = 0; + state->return_receipt = 0; + state->errors_to = 0; + state->auto_hdrs = argv_alloc(1); + state->hbc_rcpt = 0; + state->flags = 0; + state->tflags = 0; + state->qmgr_opts = 0; + state->errs = 0; + state->err_mask = 0; + state->headers_seen = 0; + state->hop_count = 0; + state->resent = ""; + state->dups = been_here_init(var_dup_filter_limit, BH_FLAG_FOLD); + state->action = cleanup_envelope; + state->data_offset = -1; + state->body_offset = -1; + state->xtra_offset = -1; + state->cont_length = 0; + state->sender_pt_offset = -1; + state->sender_pt_target = -1; + state->append_rcpt_pt_offset = -1; + state->append_rcpt_pt_target = -1; + state->append_hdr_pt_offset = -1; + state->append_hdr_pt_target = -1; + state->append_meta_pt_offset = -1; + state->append_meta_pt_target = -1; + state->rcpt_count = 0; + state->reason = 0; + state->smtp_reply = 0; + state->attr = nvtable_create(10); + nvtable_update(state->attr, MAIL_ATTR_LOG_ORIGIN, MAIL_ATTR_ORG_LOCAL); + state->mime_state = 0; + state->mime_errs = 0; + state->hdr_rewrite_context = MAIL_ATTR_RWR_LOCAL; + state->filter = 0; + state->redirect = 0; + state->dsn_envid = 0; + state->dsn_ret = 0; + state->dsn_notify = 0; + state->dsn_orcpt = 0; + state->verp_delims = 0; + state->milters = 0; + state->client_name = 0; + state->reverse_name = 0; + state->client_addr = 0; + state->client_af = 0; + state->client_port = 0; + state->server_addr = 0; + state->server_port = 0; + state->milter_ext_from = 0; + state->milter_ext_rcpt = 0; + state->milter_err_text = 0; + state->milter_dsn_buf = 0; + state->free_regions = state->body_regions = state->curr_body_region = 0; + state->smtputf8 = 0; + return (state); +} + +/* cleanup_state_free - destroy global state */ + +void cleanup_state_free(CLEANUP_STATE *state) +{ + vstring_free(state->attr_buf); + vstring_free(state->temp1); + vstring_free(state->temp2); + if (cleanup_strip_chars) + vstring_free(state->stripped_buf); + if (state->fullname) + myfree(state->fullname); + if (state->sender) + myfree(state->sender); + if (state->recip) + myfree(state->recip); + if (state->orig_rcpt) + myfree(state->orig_rcpt); + if (state->return_receipt) + myfree(state->return_receipt); + if (state->errors_to) + myfree(state->errors_to); + argv_free(state->auto_hdrs); + if (state->hbc_rcpt) + argv_free(state->hbc_rcpt); + if (state->queue_name) + myfree(state->queue_name); + if (state->queue_id) + myfree(state->queue_id); + been_here_free(state->dups); + if (state->reason) + myfree(state->reason); + if (state->smtp_reply) + myfree(state->smtp_reply); + nvtable_free(state->attr); + if (state->mime_state) + mime_state_free(state->mime_state); + if (state->filter) + myfree(state->filter); + if (state->redirect) + myfree(state->redirect); + if (state->dsn_envid) + myfree(state->dsn_envid); + if (state->dsn_orcpt) + myfree(state->dsn_orcpt); + if (state->verp_delims) + myfree(state->verp_delims); + if (state->milters) + milter_free(state->milters); + if (state->milter_ext_from) + vstring_free(state->milter_ext_from); + if (state->milter_ext_rcpt) + vstring_free(state->milter_ext_rcpt); + if (state->milter_err_text) + vstring_free(state->milter_err_text); + if (state->milter_dsn_buf) + vstring_free(state->milter_dsn_buf); + cleanup_region_done(state); + myfree((void *) state); +} diff --git a/src/cleanup/loremipsum b/src/cleanup/loremipsum new file mode 100644 index 0000000..774f86d --- /dev/null +++ b/src/cleanup/loremipsum @@ -0,0 +1,28 @@ +Sed ut perspiciatis unde omnis iste natus error sit voluptatem
+accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
+quae ab illo inventore veritatis et quasi architecto beatae vitae
+dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit
+aspernatur aut odit aut fugit, sed quia consequuntur magni dolores
+eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam
+est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci
+velit, sed quia non numquam eius modi tempora incidunt ut labore
+et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima
+veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam,
+nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure
+reprehenderit qui in ea voluptate velit esse quam nihil molestiae
+consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla
+pariatur?
+
+At vero eos et accusamus et iusto odio dignissimos ducimus qui
+blanditiis praesentium voluptatum deleniti atque corrupti quos
+dolores et quas molestias excepturi sint occaecati cupiditate non
+provident, similique sunt in culpa qui officia deserunt mollitia
+animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis
+est et expedita distinctio. Nam libero tempore, cum soluta nobis
+est eligendi optio cumque nihil impedit quo minus id quod maxime
+placeat facere possimus, omnis voluptas assumenda est, omnis dolor
+repellendus. Temporibus autem quibusdam et aut officiis debitis aut
+rerum necessitatibus saepe eveniet ut et voluptates repudiandae
+sint et molestiae non recusandae. Itaque earum rerum hic tenetur a
+sapiente delectus, ut aut reiciendis voluptatibus maiores alias
+consequatur aut perferendis doloribus asperiores repellat.
diff --git a/src/cleanup/loremipsum2 b/src/cleanup/loremipsum2 new file mode 100644 index 0000000..93cfa67 --- /dev/null +++ b/src/cleanup/loremipsum2 @@ -0,0 +1,57 @@ +Sed ut perspiciatis unde omnis iste natus error sit voluptatem
+accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
+quae ab illo inventore veritatis et quasi architecto beatae vitae
+dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit
+aspernatur aut odit aut fugit, sed quia consequuntur magni dolores
+eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam
+est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci
+velit, sed quia non numquam eius modi tempora incidunt ut labore
+et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima
+veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam,
+nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure
+reprehenderit qui in ea voluptate velit esse quam nihil molestiae
+consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla
+pariatur?
+
+At vero eos et accusamus et iusto odio dignissimos ducimus qui
+blanditiis praesentium voluptatum deleniti atque corrupti quos
+dolores et quas molestias excepturi sint occaecati cupiditate non
+provident, similique sunt in culpa qui officia deserunt mollitia
+animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis
+est et expedita distinctio. Nam libero tempore, cum soluta nobis
+est eligendi optio cumque nihil impedit quo minus id quod maxime
+placeat facere possimus, omnis voluptas assumenda est, omnis dolor
+repellendus. Temporibus autem quibusdam et aut officiis debitis aut
+rerum necessitatibus saepe eveniet ut et voluptates repudiandae
+sint et molestiae non recusandae. Itaque earum rerum hic tenetur a
+sapiente delectus, ut aut reiciendis voluptatibus maiores alias
+consequatur aut perferendis doloribus asperiores repellat.
+
+Sed ut perspiciatis unde omnis iste natus error sit voluptatem
+accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
+quae ab illo inventore veritatis et quasi architecto beatae vitae
+dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit
+aspernatur aut odit aut fugit, sed quia consequuntur magni dolores
+eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam
+est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci
+velit, sed quia non numquam eius modi tempora incidunt ut labore
+et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima
+veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam,
+nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure
+reprehenderit qui in ea voluptate velit esse quam nihil molestiae
+consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla
+pariatur?
+
+At vero eos et accusamus et iusto odio dignissimos ducimus qui
+blanditiis praesentium voluptatum deleniti atque corrupti quos
+dolores et quas molestias excepturi sint occaecati cupiditate non
+provident, similique sunt in culpa qui officia deserunt mollitia
+animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis
+est et expedita distinctio. Nam libero tempore, cum soluta nobis
+est eligendi optio cumque nihil impedit quo minus id quod maxime
+placeat facere possimus, omnis voluptas assumenda est, omnis dolor
+repellendus. Temporibus autem quibusdam et aut officiis debitis aut
+rerum necessitatibus saepe eveniet ut et voluptates repudiandae
+sint et molestiae non recusandae. Itaque earum rerum hic tenetur a
+sapiente delectus, ut aut reiciendis voluptatibus maiores alias
+consequatur aut perferendis doloribus asperiores repellat.
diff --git a/src/cleanup/test-queue-file b/src/cleanup/test-queue-file Binary files differnew file mode 100644 index 0000000..8412ae3 --- /dev/null +++ b/src/cleanup/test-queue-file diff --git a/src/cleanup/test-queue-file10 b/src/cleanup/test-queue-file10 Binary files differnew file mode 100644 index 0000000..27a9ec7 --- /dev/null +++ b/src/cleanup/test-queue-file10 diff --git a/src/cleanup/test-queue-file11 b/src/cleanup/test-queue-file11 Binary files differnew file mode 100644 index 0000000..745dc90 --- /dev/null +++ b/src/cleanup/test-queue-file11 diff --git a/src/cleanup/test-queue-file12 b/src/cleanup/test-queue-file12 Binary files differnew file mode 100644 index 0000000..4979c1d --- /dev/null +++ b/src/cleanup/test-queue-file12 diff --git a/src/cleanup/test-queue-file13a b/src/cleanup/test-queue-file13a Binary files differnew file mode 100644 index 0000000..4979c1d --- /dev/null +++ b/src/cleanup/test-queue-file13a diff --git a/src/cleanup/test-queue-file13b b/src/cleanup/test-queue-file13b Binary files differnew file mode 100644 index 0000000..4979c1d --- /dev/null +++ b/src/cleanup/test-queue-file13b diff --git a/src/cleanup/test-queue-file13c b/src/cleanup/test-queue-file13c Binary files differnew file mode 100644 index 0000000..4979c1d --- /dev/null +++ b/src/cleanup/test-queue-file13c diff --git a/src/cleanup/test-queue-file13d b/src/cleanup/test-queue-file13d Binary files differnew file mode 100644 index 0000000..745dc90 --- /dev/null +++ b/src/cleanup/test-queue-file13d diff --git a/src/cleanup/test-queue-file13e b/src/cleanup/test-queue-file13e Binary files differnew file mode 100644 index 0000000..4979c1d --- /dev/null +++ b/src/cleanup/test-queue-file13e diff --git a/src/cleanup/test-queue-file13f b/src/cleanup/test-queue-file13f Binary files differnew file mode 100644 index 0000000..4979c1d --- /dev/null +++ b/src/cleanup/test-queue-file13f diff --git a/src/cleanup/test-queue-file13g b/src/cleanup/test-queue-file13g Binary files differnew file mode 100644 index 0000000..4979c1d --- /dev/null +++ b/src/cleanup/test-queue-file13g diff --git a/src/cleanup/test-queue-file13h b/src/cleanup/test-queue-file13h Binary files differnew file mode 100644 index 0000000..4979c1d --- /dev/null +++ b/src/cleanup/test-queue-file13h diff --git a/src/cleanup/test-queue-file13i b/src/cleanup/test-queue-file13i Binary files differnew file mode 100644 index 0000000..4979c1d --- /dev/null +++ b/src/cleanup/test-queue-file13i diff --git a/src/cleanup/test-queue-file14 b/src/cleanup/test-queue-file14 Binary files differnew file mode 100644 index 0000000..e9b388a --- /dev/null +++ b/src/cleanup/test-queue-file14 diff --git a/src/cleanup/test-queue-file15 b/src/cleanup/test-queue-file15 Binary files differnew file mode 100644 index 0000000..088bdfd --- /dev/null +++ b/src/cleanup/test-queue-file15 diff --git a/src/cleanup/test-queue-file16 b/src/cleanup/test-queue-file16 Binary files differnew file mode 100644 index 0000000..3f7c5f3 --- /dev/null +++ b/src/cleanup/test-queue-file16 diff --git a/src/cleanup/test-queue-file2 b/src/cleanup/test-queue-file2 Binary files differnew file mode 100644 index 0000000..27a9ec7 --- /dev/null +++ b/src/cleanup/test-queue-file2 diff --git a/src/cleanup/test-queue-file3 b/src/cleanup/test-queue-file3 Binary files differnew file mode 100644 index 0000000..27a9ec7 --- /dev/null +++ b/src/cleanup/test-queue-file3 diff --git a/src/cleanup/test-queue-file4 b/src/cleanup/test-queue-file4 Binary files differnew file mode 100644 index 0000000..8412ae3 --- /dev/null +++ b/src/cleanup/test-queue-file4 diff --git a/src/cleanup/test-queue-file5 b/src/cleanup/test-queue-file5 Binary files differnew file mode 100644 index 0000000..d7adfb4 --- /dev/null +++ b/src/cleanup/test-queue-file5 diff --git a/src/cleanup/test-queue-file6 b/src/cleanup/test-queue-file6 Binary files differnew file mode 100644 index 0000000..27a9ec7 --- /dev/null +++ b/src/cleanup/test-queue-file6 diff --git a/src/cleanup/test-queue-file7 b/src/cleanup/test-queue-file7 Binary files differnew file mode 100644 index 0000000..27a9ec7 --- /dev/null +++ b/src/cleanup/test-queue-file7 diff --git a/src/cleanup/test-queue-file8 b/src/cleanup/test-queue-file8 Binary files differnew file mode 100644 index 0000000..27a9ec7 --- /dev/null +++ b/src/cleanup/test-queue-file8 diff --git a/src/cleanup/test-queue-file9 b/src/cleanup/test-queue-file9 Binary files differnew file mode 100644 index 0000000..27a9ec7 --- /dev/null +++ b/src/cleanup/test-queue-file9 |