Browse Source

Initial release

master
Dianne Skoll 5 months ago
commit
86ba2ef790
  1. 44
      .gitignore
  2. 156
      DIST-CONTENTS
  3. 106
      Makefile.in
  4. 13
      README.md
  5. 204
      c/Makefile.in
  6. 113
      c/config.h.in
  7. 55
      c/drop_privs.c
  8. 134
      c/dynbuf.c
  9. 32
      c/dynbuf.h
  10. 147
      c/embperl.c
  11. 680
      c/event.c
  12. 99
      c/event.h
  13. 825
      c/event_tcp.c
  14. 114
      c/event_tcp.h
  15. 51
      c/eventpriv.h
  16. 45
      c/gen_id.c
  17. 238
      c/install-sh
  18. 5014
      c/mailmunge-multiplexor.c
  19. 2988
      c/mailmunge.c
  20. 89
      c/mailmunge.h
  21. 142
      c/milter_cap.c
  22. 93
      c/milter_cap.h
  23. 678
      c/mm-mx-ctrl.c
  24. 371
      c/notifier.c
  25. 314
      c/ps_status.c
  26. 177
      c/rm_r.c
  27. 93
      c/syslog-fac.c
  28. 1357
      c/utils.c
  29. 6348
      configure
  30. 589
      configure.ac
  31. 45
      docker/Dockerfile.postfix
  32. 47
      docker/Dockerfile.sendmail
  33. 70
      docker/build-docker-container
  34. 7
      docker/docker-testfiles/etc-default-mailmunge
  35. 36
      docker/docker-testfiles/mailmunge-test-savemail.pl
  36. 6
      docker/docker-testfiles/set-apt-proxy
  37. 127
      docker/docker-testfiles/setup-tests.sh
  38. 96
      docker/run-regression-tests-on-docker
  39. 364
      docs/COPYING
  40. 7
      docs/Changelog
  41. 18
      docs/README
  42. BIN
      docs/architecture.odg
  43. 242
      docs/architecture.svg
  44. 112
      docs/style.css
  45. 78
      examples/mailmunge-filter.pl.example
  46. 238
      install-sh
  47. 284
      logo/mailmunge-logo-for-dark-background.svg
  48. 270
      logo/mailmunge-logo.svg
  49. 36
      perl/Makefile.PL
  50. 474
      perl/inc/Module/Install.pm
  51. 83
      perl/inc/Module/Install/Base.pm
  52. 154
      perl/inc/Module/Install/Can.pm
  53. 93
      perl/inc/Module/Install/Fetch.pm
  54. 418
      perl/inc/Module/Install/Makefile.pm
  55. 722
      perl/inc/Module/Install/Metadata.pm
  56. 64
      perl/inc/Module/Install/Win32.pm
  57. 63
      perl/inc/Module/Install/WriteAll.pm
  58. 314
      perl/lib/Mailmunge.pm.in
  59. 59
      perl/lib/Mailmunge/Action.pm
  60. 295
      perl/lib/Mailmunge/Action/Boilerplate.pm
  61. 306
      perl/lib/Mailmunge/Action/Stream.pm
  62. 71
      perl/lib/Mailmunge/Base.pm
  63. 114
      perl/lib/Mailmunge/Constants.pm.in
  64. 602
      perl/lib/Mailmunge/Context.pm
  65. 1825
      perl/lib/Mailmunge/Filter.pm
  66. 630
      perl/lib/Mailmunge/Filter/Compat.pm
  67. 294
      perl/lib/Mailmunge/Response.pm
  68. 41
      perl/lib/Mailmunge/Test.pm
  69. 274
      perl/lib/Mailmunge/Test/GetMX.pm
  70. 276
      perl/lib/Mailmunge/Test/SMTPForward.pm
  71. 173
      perl/lib/Mailmunge/Test/SpamAssassin.pm
  72. 204
      perl/lib/Test/Mailmunge/Filter.pm
  73. 484
      perl/lib/Test/Mailmunge/RegressionUtils.pm
  74. 327
      perl/lib/Test/Mailmunge/SMTPServer.pm
  75. 83
      perl/lib/Test/Mailmunge/Tmpdir.pm
  76. 264
      perl/lib/Test/Mailmunge/Utils.pm
  77. 30
      perl/t/00-smoketest.t
  78. 9
      perl/t/02-pod.t
  79. 12
      perl/t/03-pod-coverage.t
  80. 79
      perl/t/09-response.t
  81. 80
      perl/t/10-smtp-forward.t
  82. 36
      perl/t/20-filter-roundtrip.t
  83. 41
      perl/t/30-filter-exe.t
  84. 40
      perl/t/40-filter-everything.t
  85. 83
      perl/t/50-boilerplate.t
  86. 37
      perl/t/60-addrcpt.t
  87. 37
      perl/t/70-delrcpt.t
  88. 36
      perl/t/75-change-subject.t
  89. 36
      perl/t/76-delhdr.t
  90. 35
      perl/t/77-change-hdr.t
  91. 36
      perl/t/78-delete-all-hdrs.t
  92. 35
      perl/t/79-chgsender.t
  93. 40
      perl/t/80-add-entity.t
  94. 36
      perl/t/81-addhdr.t
  95. 40
      perl/t/90-add-part.t
  96. 38
      perl/t/91-getmx.t
  97. 3
      perl/t/data/boilerplate/RESULTS
  98. 86
      perl/t/data/boilerplate/all_atend/NEWBODY
  99. 86
      perl/t/data/boilerplate/all_atstart/NEWBODY
  100. 80
      perl/t/data/boilerplate/one_atend/NEWBODY

44
.gitignore

@ -0,0 +1,44 @@
*.bak
*.o
*~
.DS_Store
MANIFEST
Makefile
autom4te.cache
c/Makefile
confdefs.h
config.h
config.log
config.status
examples/init-script
htmldocs/
mailmunge
mailmunge-*.tar.gz
mailmunge-*.tar.gz.sig
mailmunge-filter.5
mailmunge-multiplexor
mailmunge-multiplexor.8
mailmunge-notify.7
mailmunge-protocol.7
mailmunge.8
mm-mx-ctrl
mm-mx-ctrl.8
perl/MYMETA.json
perl/MYMETA.yml
perl/blib/
perl/lib/Mailmunge.pm
perl/lib/Mailmunge/Constants.pm
perl/pm_to_blib
perl/t/tmp
pod/mailmunge-multiplexor.pod
pod/mailmunge.pod
pod/mm-mx-ctrl.pod
pod2htmd.tmp
redhat/mailmunge-init
redhat/mailmunge-sysconfig
script/mailmunge-util
script/mailmunge-util.1
systemd-units/mailmunge-multiplexor.service
systemd-units/mailmunge.service
xs_init.c
man/

156
DIST-CONTENTS

@ -0,0 +1,156 @@
.gitignore
DIST-CONTENTS
Makefile.in
README.md
c/Makefile.in
c/config.h.in
c/drop_privs.c
c/dynbuf.c
c/dynbuf.h
c/embperl.c
c/event.c
c/event.h
c/event_tcp.c
c/event_tcp.h
c/eventpriv.h
c/gen_id.c
c/install-sh
c/mailmunge-multiplexor.c
c/mailmunge.c
c/mailmunge.h
c/milter_cap.c
c/milter_cap.h
c/mm-mx-ctrl.c
c/notifier.c
c/ps_status.c
c/rm_r.c
c/syslog-fac.c
c/utils.c
configure
configure.ac
docker/Dockerfile.postfix
docker/Dockerfile.sendmail
docker/build-docker-container
docker/docker-testfiles/etc-default-mailmunge
docker/docker-testfiles/mailmunge-test-savemail.pl
docker/docker-testfiles/set-apt-proxy
docker/docker-testfiles/setup-tests.sh
docker/run-regression-tests-on-docker
docs/COPYING
docs/Changelog
docs/README
docs/architecture.odg
docs/architecture.svg
docs/style.css
examples/mailmunge-filter.pl.example
install-sh
logo/mailmunge-logo-for-dark-background.svg
logo/mailmunge-logo.svg
perl/Makefile.PL
perl/inc/Module/Install.pm
perl/inc/Module/Install/Base.pm
perl/inc/Module/Install/Can.pm
perl/inc/Module/Install/Fetch.pm
perl/inc/Module/Install/Makefile.pm
perl/inc/Module/Install/Metadata.pm
perl/inc/Module/Install/Win32.pm
perl/inc/Module/Install/WriteAll.pm
perl/lib/Mailmunge.pm.in
perl/lib/Mailmunge/Action.pm
perl/lib/Mailmunge/Action/Boilerplate.pm
perl/lib/Mailmunge/Action/Stream.pm
perl/lib/Mailmunge/Base.pm
perl/lib/Mailmunge/Constants.pm.in
perl/lib/Mailmunge/Context.pm
perl/lib/Mailmunge/Filter.pm
perl/lib/Mailmunge/Filter/Compat.pm
perl/lib/Mailmunge/Response.pm
perl/lib/Mailmunge/Test.pm
perl/lib/Mailmunge/Test/GetMX.pm
perl/lib/Mailmunge/Test/SMTPForward.pm
perl/lib/Mailmunge/Test/SpamAssassin.pm
perl/lib/Test/Mailmunge/Filter.pm
perl/lib/Test/Mailmunge/RegressionUtils.pm
perl/lib/Test/Mailmunge/SMTPServer.pm
perl/lib/Test/Mailmunge/Tmpdir.pm
perl/lib/Test/Mailmunge/Utils.pm
perl/t/00-smoketest.t
perl/t/02-pod.t
perl/t/03-pod-coverage.t
perl/t/09-response.t
perl/t/10-smtp-forward.t
perl/t/20-filter-roundtrip.t
perl/t/30-filter-exe.t
perl/t/40-filter-everything.t
perl/t/50-boilerplate.t
perl/t/60-addrcpt.t
perl/t/70-delrcpt.t
perl/t/75-change-subject.t
perl/t/76-delhdr.t
perl/t/77-change-hdr.t
perl/t/78-delete-all-hdrs.t
perl/t/79-chgsender.t
perl/t/80-add-entity.t
perl/t/81-addhdr.t
perl/t/90-add-part.t
perl/t/91-getmx.t
perl/t/data/boilerplate/RESULTS
perl/t/data/boilerplate/all_atend/NEWBODY
perl/t/data/boilerplate/all_atstart/NEWBODY
perl/t/data/boilerplate/one_atend/NEWBODY
perl/t/data/boilerplate/one_atstart/NEWBODY
perl/t/data/for-boilerplate.msg
perl/t/data/generic-msg
perl/t/data/msg-with-everything/NEWBODY
perl/t/data/msg-with-everything/RESULTS
perl/t/data/msg-with-everything/inputmsg
perl/t/data/msg-with-exe/NEWBODY
perl/t/data/msg-with-exe/RESULTS
perl/t/data/msg-with-exe/inputmsg
perl/t/filters/default-filter.pl
perl/t/filters/test-filter.pl
pod/index.pod
pod/installing.pod
pod/mailmunge-multiplexor.pod.in
pod/mailmunge-notify.pod
pod/mailmunge-protocol.pod
pod/mailmunge.pod.in
pod/mm-mx-ctrl.pod.in
pod/watch-multiple-mailmunges.pod
regression-tests/filter/mailmunge-filter.pl
regression-tests/t/01-helo.t
regression-tests/t/02-mail.t
regression-tests/t/03-rcpt.t
regression-tests/t/04-data.t
regression-tests/t/05-mta-detection.t
regression-tests/t/06-stream.t
regression-tests/t/07-boilerplate.t
regression-tests/t/08-add-recipient.t
regression-tests/t/09-del-recipient.t
regression-tests/t/10-add-entity.t
regression-tests/t/11-add-part.t
regression-tests/t/12-change-subject.t
regression-tests/t/13-delhdr.t
regression-tests/t/14-change-hdr.t
regression-tests/t/15-delete-all-hdrs.t
regression-tests/t/16-chgsender.t
regression-tests/t/17-add-hdr.t
regression-tests/t/expected/boilerplate-end-all
regression-tests/t/expected/boilerplate-end-one
regression-tests/t/expected/boilerplate-start-all
regression-tests/t/expected/boilerplate-start-one
regression-tests/t/msgs/delete-part.msg
regression-tests/t/msgs/discard-msg
regression-tests/t/msgs/eicar-msg
regression-tests/t/msgs/for-boilerplate.msg
regression-tests/t/msgs/generic-msg
regression-tests/t/msgs/gtube-msg
script/mailmunge-util.in
script/watch-multiple-mailmunges.tcl
systemd-units/mailmunge-multiplexor.service.in
systemd-units/mailmunge.service.in
sysvinit/mailmunge.in
tools/fix-anchors.pl
tools/fix-links.pl
tools/make-dist.sh
tools/make-html-docs

106
Makefile.in

@ -0,0 +1,106 @@
# DO NOT EDIT MAKEFILE; EDIT MAKEFILE.IN INSTEAD
# Makefile.in for mailmunge
# Needed for autoconf to behave properly...
datarootdir=@datarootdir@
srcdir=@srcdir@
top_srcdir=@top_srcdir@
VPATH=@srcdir@
prefix=@prefix@
exec_prefix=@exec_prefix@
sysconfdir=@sysconfdir@
CONFSUBDIR=@CONFSUBDIR@
CONFDIR=${sysconfdir}${CONFSUBDIR}
MMUSER=@MMUSER@
MMGROUP=@MMGROUP@
MANDIR=@mandir@
MINCLUDE=@MINCLUDE@
DEFS=-D_POSIX_PTHREAD_SEMANTICS \
@EMBPERLDEFS@ \
@ENABLE_DEBUGGING@ @CLEANUP_DEFS@ -DPERL_PATH=\"@PERL@\" \
-DRM=\"@RM@\" \
-DVERSION=\"@PACKAGE_VERSION@\" \
-DSPOOLDIR=\"@SPOOLDIR@\" \
-DQDIR=\"@QDIR@\" \
-DCONFDIR=\"${CONFDIR}\"
CC=@CC@
PERL=@PERL@
INSTALL=@INSTALL@
CFLAGS=@CFLAGS@
EMBPERLCFLAGS=@EMBPERLCFLAGS@
PTHREAD_FLAG=@PTHREAD_FLAG@
EMBPERLOBJS=@EMBPERLOBJS@
EMBPERLLDFLAGS=@EMBPERLLDFLAGS@
EMBPERLLIBS=@EMBPERLLIBS@
LIBS_WITHOUT_PTHREAD=@LIBS_WITHOUT_PTHREAD@
LIBS=@LIBS@
LDFLAGS=@LDFLAGS@
SPOOLDIR=@SPOOLDIR@
QDIR=@QDIR@
VERSION=@PACKAGE_VERSION@
INSTALL_STRIP_FLAG=-s
## NO MORE ./configure substitutions beyond this point!
all: c/mailmunge pod2man perl/Makefile perl/blib/lib/Mailmunge.pm
perl/Makefile:
cd perl && @PERL@ Makefile.PL
perl/blib/lib/Mailmunge.pm: perl/Makefile
$(MAKE) -C perl
c/mailmunge:
$(MAKE) -C c all
clean:: FORCE
$(MAKE) -C c clean
$(MAKE) -C perl clean
rm -f pod2htmd.tmp
rm -f perl/Makefile.old
rm -f mailmunge-*.tar.gz
rm -f pod/*~
rm -rf man
pod2man:: script/mailmunge-util.1
script/mailmunge-util.1: script/mailmunge-util
$(PERL) "-MExtUtils::Command::MM" -e pod2man "--" --section=1 --perm_rw=644 \
--center="Mailmunge Documentation" \
--release=$(VERSION) \
script/mailmunge-util script/mailmunge-util.1
distclean:: clean
rm -f config.log config.status Makefile config.cache config.h \
script/mailmunge-util \
script/mailmunge-util.1 c/Makefile c/config.h \
perl/lib/Mailmunge.pm perl/lib/Mailmunge/Constants.pm \
perl/lib/pod2htmd.tmp pod/mailmunge.pod pod/mailmunge-multiplexor.pod \
pod/mm-mx-ctrl.pod systemd-units/mailmunge-multiplexor.service \
systemd-units/mailmunge.service sysvinit/mailmunge
rm -rf autom4te.cache
install: all
$(MAKE) -C c install DESTDIR=$(DESTDIR)
$(MAKE) -C perl install DESTDIR=$(DESTDIR)
dist: FORCE
./tools/make-dist.sh $(VERSION)
htmldocs: FORCE
./tools/make-html-docs
DIST-CONTENTS: FORCE
git ls-files > DIST-CONTENTS
test: all
cd perl && prove -l $(TEST_VERBOSE) t/*.t
FORCE:
.phony: FORCE

13
README.md

@ -0,0 +1,13 @@
# Mailmunge
Mailmunge is an email filtering framework that uses the Milter library
to interface with Postfix or Sendmail. It lets you write your email
filtering policies in Perl.
The home page is https://www.mailmunge.org/ and full documentation is at
https://www.mailmunge.org/docs/
# License
Mailmunge is free software, licensed under the terms of the
GNU General Public License, version 2.

204
c/Makefile.in

@ -0,0 +1,204 @@
# DO NOT EDIT MAKEFILE; EDIT MAKEFILE.IN INSTEAD
# Makefile.in for C code.
# Needed for autoconf to behave properly...
datarootdir=@datarootdir@
srcdir=@srcdir@
top_srcdir=@top_srcdir@
VPATH=@srcdir@
prefix=@prefix@
exec_prefix=@exec_prefix@
sysconfdir=@sysconfdir@
CONFSUBDIR=@CONFSUBDIR@
CONFDIR=${sysconfdir}${CONFSUBDIR}
MMUSER=@MMUSER@
MMGROUP=@MMGROUP@
MANDIR=@mandir@
MINCLUDE=@MINCLUDE@
DEFS=-D_POSIX_PTHREAD_SEMANTICS \
@EMBPERLDEFS@ \
@ENABLE_DEBUGGING@ @CLEANUP_DEFS@ -DPERL_PATH=\"@PERL@\" \
-DRM=\"@RM@\" \
-DVERSION=\"@PACKAGE_VERSION@\" \
-DSPOOLDIR=\"@SPOOLDIR@\" \
-DQDIR=\"@QDIR@\" \
-DCONFDIR=\"${CONFDIR}\"
CC=@CC@
PERL=@PERL@
INSTALL=@INSTALL@
CFLAGS=@CFLAGS@
EMBPERLCFLAGS=@EMBPERLCFLAGS@
PTHREAD_FLAG=@PTHREAD_FLAG@
EMBPERLOBJS=@EMBPERLOBJS@
EMBPERLLDFLAGS=@EMBPERLLDFLAGS@
EMBPERLLIBS=@EMBPERLLIBS@
LIBS_WITHOUT_PTHREAD=@LIBS_WITHOUT_PTHREAD@
LIBS=@LIBS@
LDFLAGS=@LDFLAGS@
SPOOLDIR=@SPOOLDIR@
QDIR=@QDIR@
VERSION=@PACKAGE_VERSION@
INSTALL_STRIP_FLAG=-s
## NO MORE ./configure substitutions beyond this point!
all: mailmunge mailmunge-multiplexor mm-mx-ctrl manpages
mailmunge-multiplexor: mailmunge-multiplexor.o event.o event_tcp.o drop_privs_nothread.o notifier.o syslog-fac.o utils.o ps_status.o $(EMBPERLOBJS)
$(CC) $(CFLAGS) -o mailmunge-multiplexor mailmunge-multiplexor.o event.o event_tcp.o drop_privs_nothread.o syslog-fac.o notifier.o utils.o ps_status.o $(EMBPERLOBJS) $(LIBS_WITHOUT_PTHREAD) $(EMBPERLLDFLAGS) $(EMBPERLLIBS)
embperl.o: embperl.c
$(CC) $(CFLAGS) $(EMBPERLCFLAGS) $(PTHREAD_FLAG) $(DEFS) $(MINCLUDE) -c -o embperl.o $(srcdir)/embperl.c
ps_status.o: ps_status.c
$(CC) $(CFLAGS) $(EMBPERLCFLAGS) $(PTHREAD_FLAG) $(DEFS) $(MINCLUDE) -c -o ps_status.o $(srcdir)/ps_status.c
xs_init.o: xs_init.c
$(CC) $(CFLAGS) $(EMBPERLCFLAGS) $(PTHREAD_FLAG) $(DEFS) $(MINCLUDE) -c -o xs_init.o $(srcdir)/xs_init.c
xs_init.c: embperl.c
$(PERL) -MExtUtils::Embed -e xsinit -- -o $(srcdir)/xs_init.c
test-embed-perl.o: test-embed-perl.c
$(CC) $(CFLAGS) $(EMBPERLCFLAGS) $(PTHREAD_FLAG) $(DEFS) $(MINCLUDE) -c -o test-embed-perl.o $(srcdir)/test-embed-perl.c
te: test-embed-perl.o
$(CC) $(CFLAGS) -o te test-embed-perl.o $(LIBS_WITHOUT_PTHREAD) $(EMBPERLLDFLAGS) $(EMBPERLLIBS)
rm_r.o: rm_r.c
$(CC) $(CFLAGS) $(PTHREAD_FLAG) $(DEFS) $(MINCLUDE) -c -o rm_r.o $(srcdir)/rm_r.c
syslog-fac.o: syslog-fac.c
$(CC) $(CFLAGS) $(PTHREAD_FLAG) $(DEFS) $(MINCLUDE) -c -o syslog-fac.o $(srcdir)/syslog-fac.c
mm-mx-ctrl: mm-mx-ctrl.o
$(CC) $(CFLAGS) -o mm-mx-ctrl mm-mx-ctrl.o $(LIBS_WITHOUT_PTHREAD)
mm-mx-ctrl.o: mm-mx-ctrl.c
$(CC) $(CFLAGS) $(DEFS) $(MINCLUDE) -c -o mm-mx-ctrl.o $(srcdir)/mm-mx-ctrl.c
event_tcp.o: event_tcp.c
$(CC) $(CFLAGS) $(DEFS) $(MINCLUDE) -c -o event_tcp.o $(srcdir)/event_tcp.c
notifier.o: notifier.c
$(CC) $(CFLAGS) $(DEFS) $(MINCLUDE) -c -o notifier.o $(srcdir)/notifier.c
drop_privs_nothread.o: drop_privs.c
$(CC) $(CFLAGS) $(DEFS) $(MINCLUDE) -c -o drop_privs_nothread.o $(srcdir)/drop_privs.c
drop_privs_threaded.o: drop_privs.c
$(CC) $(CFLAGS) $(PTHREAD_FLAG) $(DEFS) $(MINCLUDE) -c -o drop_privs_threaded.o $(srcdir)/drop_privs.c
event.o: event.c
$(CC) $(CFLAGS) $(DEFS) $(MINCLUDE) -c -o event.o $(srcdir)/event.c
mailmunge-multiplexor.o: mailmunge-multiplexor.c
$(CC) $(CFLAGS) $(DEFS) $(MINCLUDE) -c -o mailmunge-multiplexor.o $(srcdir)/mailmunge-multiplexor.c
mailmunge: mailmunge.o drop_privs_threaded.o utils.o rm_r.o syslog-fac.o dynbuf.o milter_cap.o gen_id.o
$(CC) $(CFLAGS) $(PTHREAD_FLAG) -o mailmunge mailmunge.o drop_privs_threaded.o utils.o rm_r.o syslog-fac.o dynbuf.o milter_cap.o gen_id.o $(LDFLAGS) -lmilter $(LIBS)
mailmunge.o: mailmunge.c mailmunge.h
$(CC) $(CFLAGS) $(PTHREAD_FLAG) $(DEFS) $(MINCLUDE) -c -o mailmunge.o $(srcdir)/mailmunge.c
utils.o: utils.c mailmunge.h
$(CC) $(CFLAGS) $(PTHREAD_FLAG) $(DEFS) $(MINCLUDE) -c -o utils.o $(srcdir)/utils.c
milter_cap.o: milter_cap.c mailmunge.h
$(CC) $(CFLAGS) $(PTHREAD_FLAG) $(DEFS) $(MINCLUDE) -c -o milter_cap.o $(srcdir)/milter_cap.c
dynbuf.o: dynbuf.c dynbuf.h
$(CC) $(CFLAGS) $(PTHREAD_FLAG) $(DEFS) $(MINCLUDE) -c -o dynbuf.o $(srcdir)/dynbuf.c
gen_id.o: gen_id.c mailmunge.h
$(CC) $(CFLAGS) $(PTHREAD_FLAG) $(DEFS) $(MINCLUDE) -c -o gen_id.o $(srcdir)/gen_id.c
manpages: ../man/mailmunge-multiplexor.8 ../man/mailmunge-notify.7 ../man/mailmunge-protocol.7 ../man/mailmunge.8 ../man/mm-mx-ctrl.8 ../man/watch-multiple-mailmunges.8
../man/mailmunge-multiplexor.8: ../pod/mailmunge-multiplexor.pod
-test -d ../man || mkdir ../man > /dev/null 2>&1
pod2man < $< > $@
../man/mailmunge-notify.7: ../pod/mailmunge-notify.pod
-test -d ../man || mkdir ../man > /dev/null 2>&1
pod2man < $< > $@
../man/mailmunge-protocol.7: ../pod/mailmunge-protocol.pod
-test -d ../man || mkdir ../man > /dev/null 2>&1
pod2man < $< > $@
../man/mailmunge.8: ../pod/mailmunge.pod
-test -d ../man || mkdir ../man > /dev/null 2>&1
pod2man < $< > $@
../man/mm-mx-ctrl.8: ../pod/mm-mx-ctrl.pod
-test -d ../man || mkdir ../man > /dev/null 2>&1
pod2man < $< > $@
../man/watch-multiple-mailmunges.8: ../pod/watch-multiple-mailmunges.pod
-test -d ../man || mkdir ../man > /dev/null 2>&1
pod2man < $< > $@
clean:: FORCE
rm -f *~ *.o mailmunge mailmunge-multiplexor mm-mx-ctrl xs_init.c
install: all
$(INSTALL) -m 755 -d $(DESTDIR)$(CONFDIR)
if test "$(MMUSER)" != "" ; then \
if id "$(MMUSER)" > /dev/null 2>&1 ; then \
chown "$(MMUSER)" $(DESTDIR)${CONFDIR}/mailmunge-ip-key > /dev/null 2>&1 || true; \
test ! -d $(DESTDIR)$(SPOOLDIR) && $(INSTALL) -m 750 -o $(MMUSER) -d $(DESTDIR)$(SPOOLDIR) > /dev/null 2>&1 || true; \
test ! -d $(DESTDIR)$(QDIR) && $(INSTALL) -m 750 -o $(MMUSER) -d $(DESTDIR)$(QDIR) > /dev/null 2>&1 || true; \
fi \
else \
echo ""; \
echo "Please create the spool directory, '$(SPOOLDIR)',"; \
echo "if it does not exist. Give it mode 700 or 750, and make"; \
echo "it owned by the user and group you intend to run Mailmunge as."; \
if test "$(QDIR)" != "$(SPOOLDIR)" ; then \
echo "Please do the same with the quarantine directory, '$(QDIR)'."; \
fi; \
fi
if test "$(MMGROUP)" != "" ; then \
chgrp $(MMGROUP) $(DESTDIR)$(SPOOLDIR) > /dev/null 2>&1 || true; \
chgrp $(MMGROUP) $(DESTDIR)$(QDIR) > /dev/null 2>&1 || true; \
fi
$(INSTALL) -m 755 -d $(DESTDIR)/etc
$(INSTALL) -m 755 -d $(DESTDIR)/etc/init.d
$(INSTALL) -m 755 -d $(DESTDIR)/etc/systemd
$(INSTALL) -m 755 -d $(DESTDIR)/etc/systemd/system
$(INSTALL) -m 644 ../systemd-units/mailmunge.service $(DESTDIR)/etc/systemd/system
$(INSTALL) -m 644 ../systemd-units/mailmunge-multiplexor.service $(DESTDIR)/etc/systemd/system
$(INSTALL) -m 755 ../examples/mailmunge-filter.pl.example $(DESTDIR)/$(CONFDIR)/mailmunge-filter.pl.example
$(INSTALL) -m 755 ../sysvinit/mailmunge $(DESTDIR)/etc/init.d
$(INSTALL) -m 755 -d $(DESTDIR)$(prefix)/bin
$(INSTALL) -m 755 -d $(DESTDIR)$(MANDIR)/man1
$(INSTALL) -m 755 -d $(DESTDIR)$(MANDIR)/man7
$(INSTALL) -m 755 -d $(DESTDIR)$(MANDIR)/man8
-test ! -d $(DESTDIR)$(SPOOLDIR) && mkdir -p $(DESTDIR)$(SPOOLDIR) && chmod 750 $(DESTDIR)$(SPOOLDIR) || true
-test ! -d $(DESTDIR)$(QDIR) && mkdir -p $(DESTDIR)$(QDIR) && chmod 750 $(DESTDIR)$(QDIR) || true
$(INSTALL) -m 755 $(INSTALL_STRIP_FLAG) mailmunge-multiplexor $(DESTDIR)$(prefix)/bin/mailmunge-multiplexor
$(INSTALL) -m 755 $(INSTALL_STRIP_FLAG) mm-mx-ctrl $(DESTDIR)$(prefix)/bin/mm-mx-ctrl
$(INSTALL) -m 755 $(INSTALL_STRIP_FLAG) mailmunge $(DESTDIR)$(prefix)/bin/mailmunge
$(INSTALL) -m 755 ../script/watch-multiple-mailmunges.tcl $(DESTDIR)$(prefix)/bin/watch-multiple-mailmunges.tcl
$(INSTALL) -m 755 ../script/mailmunge-util $(DESTDIR)$(prefix)/bin/mailmunge-util
$(INSTALL) -m 644 ../script/mailmunge-util.1 $(DESTDIR)$(MANDIR)/man1/mailmunge-util.1
$(INSTALL) -m 644 ../man/watch-multiple-mailmunges.8 $(DESTDIR)$(MANDIR)/man8/watch-multiple-mailmunges.8
$(INSTALL) -m 644 ../man/mailmunge-multiplexor.8 $(DESTDIR)$(MANDIR)/man8/mailmunge-multiplexor.8
$(INSTALL) -m 644 ../man/mailmunge-notify.7 $(DESTDIR)$(MANDIR)/man7/mailmunge-notify.7
$(INSTALL) -m 644 ../man/mailmunge-protocol.7 $(DESTDIR)$(MANDIR)/man7/mailmunge-protocol.7
$(INSTALL) -m 644 ../man/mailmunge.8 $(DESTDIR)$(MANDIR)/man8/mailmunge.8
$(INSTALL) -m 644 ../man/mm-mx-ctrl.8 $(DESTDIR)$(MANDIR)/man8/mm-mx-ctrl.8
$(INSTALL) -m 644 ../man/watch-multiple-mailmunges.8 $(DESTDIR)$(MANDIR)/man8/watch-multiple-mailmunges.8
FORCE:
.phony: FORCE

113
c/config.h.in

@ -0,0 +1,113 @@
/* config.h.in. Generated from configure.ac by autoheader. */
/* Define to 1 if you have the <getopt.h> header file. */
#undef HAVE_GETOPT_H
/* Define to 1 if you have the `getpwnam_r' function. */
#undef HAVE_GETPWNAM_R
/* Define to 1 if you have the `inet_ntop' function. */
#undef HAVE_INET_NTOP
/* Define to 1 if you have the `initgroups' function. */
#undef HAVE_INITGROUPS
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the `nsl' library (-lnsl). */
#undef HAVE_LIBNSL
/* Define to 1 if you have the `pthread' library (-lpthread). */
#undef HAVE_LIBPTHREAD
/* Define to 1 if you have the `resolv' library (-lresolv). */
#undef HAVE_LIBRESOLV
/* Define to 1 if you have the `socket' library (-lsocket). */
#undef HAVE_LIBSOCKET
/* Define to 1 if the system has the type `long long int'. */
#undef HAVE_LONG_LONG_INT
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the `pathconf' function. */
#undef HAVE_PATHCONF
/* Define to 1 if you have the <poll.h> header file. */
#undef HAVE_POLL_H
/* Define to 1 if you have the `readdir_r' function. */
#undef HAVE_READDIR_R
/* Define to 1 if you have the `setrlimit' function. */
#undef HAVE_SETRLIMIT
/* "Whether we have the atomic_t variable type " */
#undef HAVE_SIG_ATOMIC_T
/* Define to 1 if you have the `snprintf' function. */
#undef HAVE_SNPRINTF
/* "Whether we have the variable type socklen_t" */
#undef HAVE_SOCKLEN_T
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* "whether uint32_t is defined" */
#undef HAVE_UINT32_T
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 if the system has the type `unsigned long long int'. */
#undef HAVE_UNSIGNED_LONG_LONG_INT
/* Define to 1 if you have the `vsnprintf' function. */
#undef HAVE_VSNPRINTF
/* Define to 1 if you have the `wait3' system call. Deprecated, you should no
longer depend upon `wait3'. */
#undef HAVE_WAIT3
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
#undef HAVE_SETPROCTITLE
#undef HAVE_PS_STRINGS

55
c/drop_privs.c

@ -0,0 +1,55 @@
/***********************************************************************
*
* drop_privs.c
*
* Defines the "drop_privs" function which switches to specified user name.
*
* Copyright (C) 2002-2003 by Roaring Penguin Software Inc.
* http://www.roaringpenguin.com
*
* This program may be distributed under the terms of the GNU General
* Public License, Version 2, or (at your option) any later version.
*
***********************************************************************/
#include "config.h"
#include <sys/types.h>
#include <unistd.h>
#include <syslog.h>
#include <grp.h>
/**********************************************************************
* %FUNCTION: drop_privs
* %ARGUMENTS:
* user -- name of user to become
* uid, gid -- uid and gid to use
* %RETURNS:
* 0 on success; -1 on failure.
* %DESCRIPTION:
* Switches uid to uid of "user"; also enters that user's group and calls
* initgroups.
***********************************************************************/
int
drop_privs(char const *user, uid_t uid, gid_t gid)
{
/* Call initgroups */
#ifdef HAVE_INITGROUPS
if (initgroups((char *) user, gid) < 0) {
syslog(LOG_ERR, "drop_privs: initgroups for '%s' failed: %m", user);
return -1;
}
#endif
/* Call setgid */
if (setgid(gid) < 0) {
syslog(LOG_ERR, "drop_privs: setgid for '%s' failed: %m", user);
return -1;
}
/* Finally, call setuid */
if (setuid(uid) < 0) {
syslog(LOG_ERR, "drop_privs: setuid for '%s' failed: %m", user);
return -1;
}
return 0;
}

134
c/dynbuf.c

@ -0,0 +1,134 @@
/***************************************************************/
/* */
/* DYNBUF.C */
/* */
/* Implementation of functions for manipulating dynamic */
/* buffers. */
/* */
/* This file was part of REMIND. */
/* Copyright (C) 1992-1998 by Dianne Skoll */
/* Copyright (C) 1999-2007 by Roaring Penguin Software Inc. */
/* */
/***************************************************************/
#include "dynbuf.h"
#include <stdlib.h>
#include <string.h>
/**********************************************************************
%FUNCTION: dbuf_makeroom
%ARGUMENTS:
dbuf -- pointer to a dynamic buffer
n -- size to expand to
%RETURNS:
0 if all went well, -1 otherwise.
%DESCRIPTION:
Doubles the size of dynamic buffer until it has room for at least
'n' characters, not including trailing '\0'
**********************************************************************/
static int
dbuf_makeroom(dynamic_buffer *dbuf, int n)
{
/* Double size until it's greater than n (strictly > to leave room
for trailing '\0' */
int size = dbuf->allocated_len;
char *buf;
if (size > n) return 0;
while (size <= n) {
size *= 2;
}
/* Allocate memory */
buf = (char *) malloc(size);
if (!buf) return -1;
/* Copy contents */
strcpy(buf, dbuf->buffer);
/* Free old contents if necessary */
if (dbuf->buffer != dbuf->static_buf) free(dbuf->buffer);
dbuf->buffer = buf;
dbuf->allocated_len = size;
return 0;
}
/**********************************************************************
%FUNCTION: dbuf_init
%ARGUMENTS:
dbuf -- pointer to a dynamic buffer
%RETURNS:
Nothing
%DESCRIPTION:
Initializes a dynamic buffer
**********************************************************************/
void
dbuf_init(dynamic_buffer *dbuf)
{
dbuf->buffer = dbuf->static_buf;
dbuf->len = 0;
dbuf->allocated_len = DBUF_STATIC_SIZE;
dbuf->buffer[0] = 0;
}
/**********************************************************************
%FUNCTION: dbuf_putc
%ARGUMENTS:
dbuf -- pointer to a dynamic buffer
c -- character to append to buffer
%RETURNS:
0 if all went well; -1 if out of memory
%DESCRIPTION:
Appends a character to the buffer.
**********************************************************************/
int
dbuf_putc(dynamic_buffer *dbuf, char const c)
{
if (dbuf->allocated_len <= dbuf->len+1) {
if (dbuf_makeroom(dbuf, dbuf->len+1) != 0) return -1;
}
dbuf->buffer[dbuf->len++] = c;
dbuf->buffer[dbuf->len] = 0;
return 0;
}
/**********************************************************************
%FUNCTION: dbuf_puts
%ARGUMENTS:
dbuf -- pointer to a dynamic buffer
str -- string to append to buffer
%RETURNS:
OK if all went well; E_NO_MEM if out of memory
%DESCRIPTION:
Appends a string to the buffer.
**********************************************************************/
int
dbuf_puts(dynamic_buffer *dbuf, char const *str)
{
int l = strlen(str);
if (!l) return 0;
if (dbuf->allocated_len <= dbuf->len + l) {
if (dbuf_makeroom(dbuf, dbuf->len+l) != 0) return -1;
}
strcpy((dbuf->buffer+dbuf->len), str);
dbuf->len += l;
return 0;
}
/**********************************************************************
%FUNCTION: dbuf_free
%ARGUMENTS:
dbuf -- pointer to a dynamic buffer
%RETURNS:
Nothing
%DESCRIPTION:
Frees and reinitializes a dynamic buffer
**********************************************************************/
void
dbuf_free(dynamic_buffer *dbuf)
{
if (dbuf->buffer != dbuf->static_buf) free(dbuf->buffer);
dbuf_init(dbuf);
}

32
c/dynbuf.h

@ -0,0 +1,32 @@
/***************************************************************/
/* */
/* DYNBUF.H */
/* */
/* Declaration of functions for manipulating dynamic buffers */
/* */
/* This file was part of REMIND. */
/* Copyright (C) 1992-1998 by Dianne Skoll */
/* Copyright (C) 1999-2007 by Roaring Penguin Software Inc. */
/* */
/***************************************************************/
#ifndef DYNBUF_H
#define DYNBUF_H
#define DBUF_STATIC_SIZE 4096
typedef struct {
char *buffer;
int len;
int allocated_len;
char static_buf[DBUF_STATIC_SIZE];
} dynamic_buffer;
void dbuf_init(dynamic_buffer *dbuf);
int dbuf_putc(dynamic_buffer *dbuf, char const c);
int dbuf_puts(dynamic_buffer *dbuf, char const *str);
void dbuf_free(dynamic_buffer *dbuf);
#define DBUF_VAL(buf_ptr) ((buf_ptr)->buffer)
#define DBUF_LEN(buf_ptr) ((buf_ptr)->len)
#endif /* DYNBUF_H */

147
c/embperl.c

@ -0,0 +1,147 @@
/***********************************************************************
*
* embperl.c
*
* Routines for manipulating embedded Perl interpreter
*
* Copyright (C) 2003 by Roaring Penguin Software Inc.
*
***********************************************************************/
#ifdef EMBED_PERL
#include "config.h"
#include <EXTERN.h>
#include <perl.h>
#include <errno.h>
#include <syslog.h>
#ifdef PERL_SET_CONTEXT
#define PSC(x) PERL_SET_CONTEXT(x)
#else
#define PSC(x) (void) 0
#endif
#define PERLPARSE_NUM_ARGS 6
static PerlInterpreter *my_perl = NULL;
extern void xs_init (pTHX);
void
init_embedded_interpreter(int argc, char **argv, char **env)
{
#ifdef PERL_SYS_INIT3
PERL_SYS_INIT3(&argc, &argv, &env);
#endif
}
void
term_embedded_interpreter(void)
{
if (my_perl != NULL) {
PSC(my_perl);
PERL_SET_INTERP(my_perl);
PL_perl_destruct_level = 1;
perl_destruct(my_perl);
perl_free(my_perl);
#ifdef PERL_SYS_TERM
PERL_SYS_TERM();
#endif
my_perl = NULL;
}
}
static char **argv = NULL;
int
make_embedded_interpreter(char const *progPath,
int wantStatusReports,
char **env)
{
int argc;
/* Why do we malloc argv instead of making it static? Because on some
systems, Perl makes horrendously evil assumptions about the alignment
of argv... we use malloc to get guaranteed worst-case alignment.
Yes, the Perl innards are completely horrible. */
if (!argv) {
argv = (char **) malloc(PERLPARSE_NUM_ARGS * sizeof(char *));
if (!argv) {
fprintf(stderr, "Out of memory allocating argv[] array for embedded Perl!");
syslog(LOG_ERR, "Out of memory allocating argv[] array for embedded Perl!");
exit(EXIT_FAILURE);
}
}
memset(argv, 0, PERLPARSE_NUM_ARGS * sizeof(char *));
if (my_perl != NULL) {
#ifdef SAFE_EMBED_PERL
PSC(my_perl);
PERL_SET_INTERP(my_perl);
PL_perl_destruct_level = 1;
perl_destruct(my_perl);
perl_free(my_perl);
my_perl = NULL;
#else
syslog(LOG_WARNING, "Cannot destroy and recreate a Perl interpreter safely on this platform. Filter rules will NOT be reread.");
return 0;
#endif
}
argv[0] = "";
argv[1] = (char *) progPath;
if (wantStatusReports) {
argv[2] = "-embserveru";
} else {
argv[2] = "-embserver";
}
argv[3] = NULL;
argc = 3;
my_perl = perl_alloc();
if (!my_perl) {
errno = ENOMEM;
return -1;
}
PSC(my_perl);
PERL_SET_INTERP(my_perl);
PL_perl_destruct_level = 1;
perl_construct(my_perl);
perl_parse(my_perl, xs_init, argc, argv, NULL);
perl_run(my_perl);
return 0;
}
/* Perl caches $$ so the PID is wrong after we fork. This
routine fixes it up */
static void
embperl_fix_pid(void)
{
GV *tmpgv;
if ((tmpgv = gv_fetchpv("$",TRUE, SVt_PV))) {
SvREADONLY_off(GvSV(tmpgv));
sv_setiv(GvSV(tmpgv), PerlProc_getpid());
SvREADONLY_on(GvSV(tmpgv));
}
}
extern void init_ps_display(char const *fixed_part);
extern void set_ps_display(char const *activity);
void
run_embedded_filter(int workerno)
{
char *args[] = { NULL };
char buf[64];
snprintf(buf, sizeof(buf), "mailmunge: Filtering worker %d", workerno);
init_ps_display(buf);
set_ps_display("");
PSC(my_perl);
PERL_SET_INTERP(my_perl);
embperl_fix_pid();
perl_call_argv("_mailmunge_do_main_loop", G_DISCARD | G_NOARGS, args);
}
#endif

680
c/event.c

@ -0,0 +1,680 @@
/***********************************************************************
*
* event.c
*
* Abstraction of select call into "event-handling" to make programming
* easier.
*
* Copyright (C) 2001-2003 Roaring Penguin Software Inc.
*
* This program may be distributed according to the terms of the GNU
* General Public License, version 2 or (at your option) any later version.
*
* Copyright (C) 2001 by Roaring Penguin Software Inc.
*
***********************************************************************/
#include "event.h"
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
static void DestroySelector(EventSelector *es);
static void DestroyHandler(EventHandler *eh);
static void DoPendingChanges(EventSelector *es);
static struct pollfd *Pollfds = NULL;
static EventHandler **Fd_to_eh = NULL;
static int Size_of_pollfds = 0;
static int adjust_pollfds(int num) {
int new_size = Size_of_pollfds * 2;
if (new_size < num) new_size = num;
if (new_size < 16) new_size = 16;
Pollfds = realloc(Pollfds, (size_t) new_size * sizeof(struct pollfd));
if (!Pollfds) {
return -1;
}
Fd_to_eh = realloc(Fd_to_eh, (size_t) new_size * sizeof(EventHandler *));
if (!Fd_to_eh) {
return -1;
}
Size_of_pollfds = new_size;
return 0;
}
/**********************************************************************
* %FUNCTION: set_cloexec
* %ARGUMENTS:
* fd -- file descriptor
* %RETURNS:
* Nothing
* %DESCRIPTION:
* Sets the FD_CLOEXEC flag on descriptor
***********************************************************************/
void
set_cloexec(int fd)
{
int flags;
flags = fcntl(fd, F_GETFD);
if (flags >= 0) {
flags |= FD_CLOEXEC;
fcntl(fd, F_SETFD, flags);
}
}
/**********************************************************************
* %FUNCTION: set_nonblocking
* %ARGUMENTS:
* fd -- file descriptor
* %RETURNS:
* 0 on success, -1 on failure
* %DESCRIPTION:
* Sets the O_NONBLOCK flag on fd
***********************************************************************/
int
set_nonblocking(int fd)
{
int flags = fcntl(fd, F_GETFL, 0);
if (flags < 0) return flags;
if (!(flags & O_NONBLOCK)) {
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
return -1;
}
}
return 0;
}
/**********************************************************************
* %FUNCTION: Event_CreateSelector
* %ARGUMENTS:
* None
* %RETURNS:
* A newly-allocated EventSelector, or NULL if out of memory.
* %DESCRIPTION:
* Creates a new EventSelector.
***********************************************************************/
EventSelector *
Event_CreateSelector(void)
{
EventSelector *es = malloc(sizeof(EventSelector));
if (!es) return NULL;
es->handlers = NULL;
es->nestLevel = 0;
es->destroyPending = 0;
es->opsPending = 0;
EVENT_DEBUG(("CreateSelector() -> %p\n", (void *) es));
return es;
}
/**********************************************************************
* %FUNCTION: Event_DestroySelector
* %ARGUMENTS:
* es -- EventSelector to destroy
* %RETURNS:
* Nothing
* %DESCRIPTION:
* Destroys an EventSelector. Destruction may be delayed if we
* are in the HandleEvent function.
***********************************************************************/
void
Event_DestroySelector(EventSelector *es)
{
if (es->nestLevel) {
es->destroyPending = 1;
es->opsPending = 1;
return;
}
DestroySelector(es);
}
/**********************************************************************
* %FUNCTION: Event_HandleEventUsingPoll
* %ARGUMENTS:
* es -- EventSelector
* %RETURNS:
* 0 if OK, non-zero on error. errno is set appropriately.
* %DESCRIPTION:
* Handles a single event (uses poll() to wait for an event.)
***********************************************************************/
int
Event_HandleEventUsingPoll(EventSelector *es)
{
struct timeval abs_timeout, now;
struct timeval timeout;
int tm;
EventHandler *eh;
int r = 0;
int errno_save = 0;
int foundTimeoutEvent = 0;
int pastDue;
int num_handlers = 0;
int nfds;
int i;
EVENT_DEBUG(("Enter Event_HandleEventUsingPoll(es=%p)\n", (void *) es));
/* Silence stupid compiler warning */
abs_timeout.tv_sec = 0;
abs_timeout.tv_usec = 0;
/* Build the pollfds array */
eh = es->handlers;
while(eh) {
num_handlers++;
eh = eh->next;
}
if (num_handlers > Size_of_pollfds) {
if (adjust_pollfds(num_handlers) < 0) {
return -1;
}
}
eh = es->handlers;
nfds = 0;
for (eh=es->handlers; eh; eh=eh->next) {
if (eh->flags & EVENT_FLAG_DELETED) continue;
eh->pollflags = 0;
if (eh->flags & (EVENT_FLAG_READABLE|EVENT_FLAG_WRITEABLE)) {
Pollfds[nfds].fd = eh->fd;
Pollfds[nfds].events = 0;
Fd_to_eh[nfds] = eh;
if (eh->flags & EVENT_FLAG_READABLE) {
Pollfds[nfds].events |= POLLIN;
}
if (eh->flags & EVENT_FLAG_WRITEABLE) {
Pollfds[nfds].events |= POLLOUT;
}
nfds++;
}
if (eh->flags & EVENT_TIMER_BITS) {
if (!foundTimeoutEvent) {
abs_timeout = eh->tmout;
foundTimeoutEvent = 1;
} else {
if (eh->tmout.tv_sec < abs_timeout.tv_sec ||
(eh->tmout.tv_sec == abs_timeout.tv_sec &&
eh->tmout.tv_usec < abs_timeout.tv_usec)) {
abs_timeout = eh->tmout;
}
}
}
}
if (foundTimeoutEvent) {
gettimeofday(&now, NULL);
/* Convert absolute timeout to relative timeout for poll */
timeout.tv_usec = abs_timeout.tv_usec - now.tv_usec;
timeout.tv_sec = abs_timeout.tv_sec - now.tv_sec;
if (timeout.tv_usec < 0) {
timeout.tv_usec += 1000000;
timeout.tv_sec--;
}
if (timeout.tv_sec < 0 ||
(timeout.tv_sec == 0 && timeout.tv_usec < 0)) {
timeout.tv_sec = 0;
timeout.tv_usec = 0;
}
tm = (timeout.tv_sec * 1000) + (timeout.tv_usec / 1000);
} else {
tm = -1;
}
if (nfds || foundTimeoutEvent) {
for(;;) {
r = poll(Pollfds, nfds, tm);
if (r < 0) {
if (errno == EINTR) continue;
}
break;
}
}
if (foundTimeoutEvent) {
gettimeofday(&now, NULL);
}
errno_save = errno;
es->nestLevel++;
if (r >= 0) {
/* Go through poll set and set pollflags */
for (i=0; i<nfds; i++) {
eh = Fd_to_eh[i];
if (Pollfds[i].revents & (POLLIN|POLLHUP)) {
if (eh->flags & EVENT_FLAG_READABLE) {
eh->pollflags |= EVENT_FLAG_READABLE;
}
}
if (Pollfds[i].revents & POLLOUT) {
if (eh->flags & EVENT_FLAG_WRITEABLE) {
eh->pollflags |= EVENT_FLAG_WRITEABLE;
}
}
}
/* Call handlers */
for (eh=es->handlers; eh; eh=eh->next) {
/* Pending delete for this handler? Ignore it */
if (eh->flags & EVENT_FLAG_DELETED) continue;
if (eh->flags & EVENT_TIMER_BITS) {
pastDue = (eh->tmout.tv_sec < now.tv_sec ||
(eh->tmout.tv_sec == now.tv_sec &&
eh->tmout.tv_usec <= now.tv_usec));
if (pastDue) {
eh->pollflags |= EVENT_TIMER_BITS;
if (eh->flags & EVENT_FLAG_TIMER) {
/* Timer events are only called once */
es->opsPending = 1;
eh->flags |= EVENT_FLAG_DELETED;
}
}
}
/* Do callback */
if (eh->pollflags) {
EVENT_DEBUG(("Enter callback: eh=%p flags=%u\n", eh, eh->pollflags));
eh->fn(es, eh->fd, eh->pollflags, eh->data);
EVENT_DEBUG(("Leave callback: eh=%p flags=%u\n", eh, eh->pollflags));
}
}
}
es->nestLevel--;
if (!es->nestLevel && es->opsPending) {
DoPendingChanges(es);
}
errno = errno_save;
return r;
}
/**********************************************************************
* %FUNCTION: Event_AddHandler
* %ARGUMENTS:
* es -- event selector
* fd -- file descriptor to watch
* flags -- combination of EVENT_FLAG_READABLE and EVENT_FLAG_WRITEABLE
* fn -- callback function to call when event is triggered
* data -- extra data to pass to callback function
* %RETURNS:
* A newly-allocated EventHandler, or NULL.
***********************************************************************/
EventHandler *
Event_AddHandler(EventSelector *es,
int fd,
unsigned int flags,
EventCallbackFunc fn,
void *data)
{
EventHandler *eh;
/* Specifically disable timer and deleted flags */
flags &= (~(EVENT_TIMER_BITS | EVENT_FLAG_DELETED));
/* Bad file descriptor */
if (fd < 0) {
errno = EBADF;
return NULL;
}
eh = malloc(sizeof(EventHandler));
if (!eh) return NULL;
eh->fd = fd;
eh->flags = flags;
eh->tmout.tv_usec = 0;
eh->tmout.tv_sec = 0;
eh->fn = fn;
eh->data = data;
/* Add immediately. This is safe even if we are in a handler. */
eh->next = es->handlers;
es->handlers = eh;
EVENT_DEBUG(("Event_AddHandler(es=%p, fd=%d, flags=%u) -> %p\n", es, fd, flags, eh));
return eh;
}
/**********************************************************************
* %FUNCTION: Event_AddHandlerWithTimeout
* %ARGUMENTS:
* es -- event selector
* fd -- file descriptor to watch
* flags -- combination of EVENT_FLAG_READABLE and EVENT_FLAG_WRITEABLE
* t -- Timeout after which to call handler, even if not readable/writable.
* If t.tv_sec < 0, calls normal Event_AddHandler with no timeout.
* fn -- callback function to call when event is triggered
* data -- extra data to pass to callback function
* %RETURNS:
* A newly-allocated EventHandler, or NULL.
***********************************************************************/
EventHandler *
Event_AddHandlerWithTimeout(EventSelector *es,
int fd,
unsigned int flags,
struct timeval t,
EventCallbackFunc fn,
void *data)
{
EventHandler *eh;
struct timeval now;
/* If timeout is negative, just do normal non-timing-out event */
if (t.tv_sec < 0 || t.tv_usec < 0) {
return Event_AddHandler(es, fd, flags, fn, data);
}
/* Specifically disable timer and deleted flags */
flags &= (~(EVENT_FLAG_TIMER | EVENT_FLAG_DELETED));
flags |= EVENT_FLAG_TIMEOUT;
/* Bad file descriptor? */
if (fd < 0) {
errno = EBADF;
return NULL;
}
/* Bad timeout? */
if (t.tv_usec >= 1000000) {
errno = EINVAL;
return NULL;
}
eh = malloc(sizeof(EventHandler));
if (!eh) return NULL;
/* Convert time interval to absolute time */
gettimeofday(&now, NULL);
t.tv_sec += now.tv_sec;
t.tv_usec += now.tv_usec;
if (t.tv_usec >= 1000000) {
t.tv_usec -= 1000000;
t.tv_sec++;
}
eh->fd = fd;
eh->flags = flags;
eh->tmout = t;
eh->fn = fn;
eh->data = data;
/* Add immediately. This is safe even if we are in a handler. */
eh->next = es->handlers;
es->handlers = eh;
EVENT_DEBUG(("Event_AddHandlerWithTimeout(es=%p, fd=%d, flags=%u, t=%d/%d) -> %p\n", es, fd, flags, t.tv_sec, t.tv_usec, eh));
return eh;
}
/**********************************************************************
* %FUNCTION: Event_AddTimerHandler
* %ARGUMENTS:
* es -- event selector
* t -- time interval after which to trigger event
* fn -- callback function to call when event is triggered
* data -- extra data to pass to callback function
* %RETURNS:
* A newly-allocated EventHandler, or NULL.
***********************************************************************/
EventHandler *
Event_AddTimerHandler(EventSelector *es,
struct timeval t,
EventCallbackFunc fn,
void *data)
{
EventHandler *eh;
struct timeval now;
/* Check time interval for validity */
if (t.tv_sec < 0 || t.tv_usec < 0 || t.tv_usec >= 1000000) {
errno = EINVAL;
return NULL;
}
eh = malloc(sizeof(EventHandler));
if (!eh) return NULL;
/* Convert time interval to absolute time */
gettimeofday(&now, NULL);
t.tv_sec += now.tv_sec;
t.tv_usec += now.tv_usec;
if (t.tv_usec >= 1000000) {
t.tv_usec -= 1000000;
t.tv_sec++;
}
eh->fd = -1;
eh->flags = EVENT_FLAG_TIMER;
eh->tmout = t;
eh->fn = fn;
eh->data = data;
/* Add immediately. This is safe even if we are in a handler. */
eh->next = es->handlers;
es->handlers = eh;
EVENT_DEBUG(("Event_AddTimerHandler(es=%p, t=%d/%d) -> %p\n", es, t.tv_sec,t.tv_usec, eh));
return eh;
}
/**********************************************************************
* %FUNCTION: Event_DelHandler
* %ARGUMENTS:
* es -- event selector
* eh -- event handler
* %RETURNS:
* 0 if OK, non-zero if there is an error
* %DESCRIPTION:
* Deletes the event handler eh
***********************************************************************/
int
Event_DelHandler(EventSelector *es,
EventHandler *eh)
{
/* Scan the handlers list */
EventHandler *cur, *prev;
EVENT_DEBUG(("Event_DelHandler(es=%p, eh=%p)\n", es, eh));
for (cur=es->handlers, prev=NULL; cur; prev=cur, cur=cur->next) {
if (cur == eh) {
if (es->nestLevel) {
eh->flags |= EVENT_FLAG_DELETED;
es->opsPending = 1;
return 0;
} else {
if (prev) prev->next = cur->next;
else es->handlers = cur->next;
DestroyHandler(cur);
return 0;
}
}
}
/* Handler not found */
return 1;
}
/**********************************************************************
* %FUNCTION: DestroySelector
* %ARGUMENTS:
* es -- an event selector
* %RETURNS:
* Nothing
* %DESCRIPTION:
* Destroys selector and all associated handles.
***********************************************************************/
static void
DestroySelector(EventSelector *es)
{
EventHandler *cur, *next;
for (cur=es->handlers; cur; cur=next) {
next = cur->next;
DestroyHandler(cur);
}
free(es);
}
/**********************************************************************
* %FUNCTION: DestroyHandler
* %ARGUMENTS:
* eh -- an event handler
* %RETURNS:
* Nothing
* %DESCRIPTION:
* Destroys handler
***********************************************************************/
static void
DestroyHandler(EventHandler *eh)
{
EVENT_DEBUG(("DestroyHandler(eh=%p)\n", eh));
free(eh);
}
/**********************************************************************
* %FUNCTION: DoPendingChanges
* %ARGUMENTS:
* es -- an event selector
* %RETURNS:
* Nothing
* %DESCRIPTION:
* Makes all pending insertions and deletions happen.
***********************************************************************/
static void
DoPendingChanges(EventSelector *es)
{
EventHandler *cur, *prev, *next;