pax_global_header00006660000000000000000000000064137252556050014524gustar00rootroot0000000000000052 comment=b998919164cd80e8475f52ed083be9e164b548e1 amavisd-milter-1.7.1/000077500000000000000000000000001372525560500144505ustar00rootroot00000000000000amavisd-milter-1.7.1/.circleci/000077500000000000000000000000001372525560500163035ustar00rootroot00000000000000amavisd-milter-1.7.1/.circleci/config.yml000066400000000000000000000045721372525560500203030ustar00rootroot00000000000000version: 2.1 jobs: build: docker: - image: circleci/buildpack-deps:stretch steps: - checkout - deploy: name: Install build prerequisities command: | sudo apt-get update sudo apt-get install \ autoconf \ automake \ libmilter-dev \ pandoc - deploy: name: Configure command: | export AMAVISD_MILTER_VERSION="${CIRCLE_TAG}" sh autoconf.sh.in ./configure - deploy: name: Build command: | make all - deploy: name: Tarball command: | make dist - persist_to_workspace: root: "." paths: - "amavisd-milter-*.tar.gz" release: docker: - image: cibuilds/github:0.12 steps: - attach_workspace: at: /tmp/workspace - run: name: "Publish Release on GitHub" command: | if [ ! -f /tmp/workspace/amavisd-milter-${CIRCLE_TAG}.tar.gz ]; then echo "ERROR: Release file amavisd-milter-${CIRCLE_TAG}.tar.gz does not exists" exit 1 fi ghr \ -t ${GITHUB_TOKEN} \ -u ${CIRCLE_PROJECT_USERNAME} \ -r ${CIRCLE_PROJECT_REPONAME} \ -c ${CIRCLE_SHA1} \ -n amavisd-milter-${CIRCLE_TAG} \ -delete \ -draft \ -prerelease \ ${CIRCLE_TAG} \ /tmp/workspace/amavisd-milter-${CIRCLE_TAG}.tar.gz workflows: version: 2 main: jobs: - build: filters: # Required since `release` has tag filters AND requires `build` # https://circleci.com/docs/2.0/workflows/#git-tag-job-execution tags: only: /.*/ - release: requires: - build filters: branches: # Release only from the master branch ignore: /.*/ tags: # Release only if it is a SemVer tag # https://github.com/semver/semver/issues/232#issuecomment-431199333 only: /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:((0|[1-9]\d*|\d*[A-Z-a-z-][\dA-Za-z-]*))(\.(0|[1-9]\d*|\d*[A-Za-z-][\dA-Za-z-]*))*))?(?:\+(?:[\dA-Za-z-]+(\.[\dA-Za-z-]*)*))?$/ amavisd-milter-1.7.1/.editorconfig000066400000000000000000000007071372525560500171310ustar00rootroot00000000000000# EditorConfig is awesome: https://EditorConfig.org # top-most EditorConfig file root = true # 2 spaces indentation, Unix-style newlines with a newline ending every file [*] end_of_line = lf charset = utf-8 indent_size = 2 indent_style = space insert_final_newline = true trim_trailing_whitespace = true # Tab indentation [{Makefile,Makefile.*}] indent_size = 8 indent_style = tab # 4 spaces indentation [*.{h,c}] indent_size = 4 indent_style = space amavisd-milter-1.7.1/.gitignore000066400000000000000000000024611372525560500164430ustar00rootroot00000000000000# Created by https://www.gitignore.io/api/c,autotools # Edit at https://www.gitignore.io/?templates=c,autotools ### Autotools ### # http://www.gnu.org/software/automake Makefile.in /ar-lib /mdate-sh /py-compile /test-driver /ylwrap # http://www.gnu.org/software/autoconf autom4te.cache /autoscan.log /autoscan-*.log /aclocal.m4 /compile /config.guess /config.h.in /config.log /config.status /config.sub /configure /configure.scan /depcomp /install-sh /missing /stamp-h1 # https://www.gnu.org/software/libtool/ /ltmain.sh # http://www.gnu.org/software/texinfo /texinfo.tex # http://www.gnu.org/software/m4/ m4/libtool.m4 m4/ltoptions.m4 m4/ltsugar.m4 m4/ltversion.m4 m4/lt~obsolete.m4 ### Autotools Patch ### ### C ### # Prerequisites *.d # Object files *.o *.ko *.obj *.elf # Linker output *.ilk *.map *.exp # Precompiled Headers *.gch *.pch # Libraries *.lib *.a *.la *.lo # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables *.exe *.out *.app *.i*86 *.x86_64 *.hex # Debug files *.dSYM/ *.su *.idb *.pdb # Kernel Module Compile Results *.mod* *.cmd .tmp_versions/ modules.order Module.symvers Mkfile.old dkms.conf # End of https://www.gitignore.io/api/c,autotools # Ignore amavid-milter generated files amavisd-milter-*.tar.gz autoconf.sh config.h configure.lineno Makefile README amavisd-milter-1.7.1/AMAVISD-MILTER.md000066400000000000000000000167231372525560500170610ustar00rootroot00000000000000# amavisd-milter(8) ## NAME **amavisd-milter** - sendmail milter for amavis ## SYNOPSIS **amavisd-milter** [**-Bfhv**] [**-d** *debug-level*] [**-D** *delivery-care-of*] [**-m** *max-conns*] [**-M** *max-wait*] [**-p** *pidfile*] [**-P**] [**-q** *backlog*] [**-s** *socket*] [**-t** *timeout*] [**-S** *socket*] [**-T** *timeout*] [**-w** *directory*] ## DESCRIPTION The amavisd-milter is a sendmail milter (mail filter) for amavis 2.4.3 and above and sendmail 8.13 and above. With the amavisd-milter, a full amavis functionality is available, including adding spam and virus information header fields, modifying the Subject, adding address extensions and removing certain recipients from delivery, while delivering the same message to the rest. For more information you can visit amavisd-milter website: > https://github.com/prehor/amavisd-milter ### Options The options are as follows: **-B** : Uses the milter macro *{daemon_name}* as the policy bank name (see [POLICY BANKS](#policy-banks) below). **-d** *debug-level* : Set the debug level. The debugging traces become more detailed as the debug level increases. Maximum is 9. **-D** *delivery-care-of* : Set AM.PDP request attribute *delivery_care_of* to *client* (default) or *server*. When the *client* method is used, then amavisd-milter is responsible for forwarding the message to the recipients. This method does not allow personalized header or body modification. When the *server* method is used, then amavis is responsible for forwarding the message to the recipients and may personalize the headers and the body of the messages. *$forward_method* variable in **amavisd.conf** must point to a place willing to accept the message without further checking in amavis. **-f** : Run amavisd-milter in the foreground (i.e. do not daemonize). Print debuging messages to the terminal. **-h** : Print the help page and exit. **-m** *max-conns* : Maximum concurrent amavis connections (default 0 = unlimited number of connections). It must be the same as the *$max_servers* variable in **amavisd.conf**. **-M** *timeout* : Timeout for message processing in seconds (default 300 seconds = 5 minutes). Must be less then timeout for a response to the final "." that terminates a message on sending MTA. **Sendmail** uses default value 1 hour, **postfix** 10 minutes and **qmail** 20 minutes. Recommended value is less than 10 minutes. If you use other milters (especially time-consuming), the timeout must be sufficient to process message in all milters. **-p** *pidfile* : Use this pid file. **-P** : When the amavis fails, the message will be passed through unchecked. **-q** *backlog* : Sets the incoming socket backlog used by **listen(2)**. If it is not set or set to zero, the operating system default is used. **-s** *socket* : Communication socket between sendmail and amavisd-milter. The protocol spoken over this socket is *MILTER* (Mail FILTER). It must have the same vale as the *INPUT_MAIL_FILTER* macro in **sendmail.mc**. The *socket* must be in format *proto:address*: > * *{unix|local}:/path/to/file* - A named pipe. > * *inet:port@{hostname|ip-address}* - An IPV4 socket. > * *inet6:port@{hostname|ip-address}* - An IPV6 socket. **-S** *socket* : Communication socket between amavisd-milter and amavis. The protocol spoken over this socket is *AM.PDP* (AMavis Policy Delegation Protocol). It must have the same value as the *$unix_socketname* variable in **amavisd.conf**. The *socket* must be in format *proto:address*: > * *{unix|local}:/path/to/file* - A named pipe. > * *inet:port@{hostname|ip-address}* - An IPV4 socket. > * *inet6:port@{hostname|ip-address}* - An IPV6 socket. **-t** *timeout* : Sendmail connection timeout in seconds (default 600 = 10 minutes). It must have the same vale as the *INPUT_MAIL_FILTER* macro in **sendmail.mc** and must be greater than or equal to the amavis connection timeout. If you use other milters (especially time-consuming), the timeout must be sufficient to process message in all milters. **-T** *timeout* : Amavis connection timeout in seconds (default 600 = 10 minutes). Must be sufficient to process message in amavis. Usually, it is a good idea to set them to the same value as sendmail connection timeout. **-v** : Report the version number and exit. **-w** *directory* : Set working directory. ## POLICY BANKS If the option **-B** is enabled, amavisd-milter uses the value of the milter macro *{daemon_name}* as the name of the amavis policy bank. Usualy, this milter macro is set to name of the MTA. When remote client is authenticated, amavisd-milter uses authentication information as the name of the amavis policy banks: **`SMTP_AUTH`** : Remote client has been authenticated. **`SMTP_AUTH_`** : The remote client authentication mechanism. **`SMTP_AUTH__`** : The number of bits used for the key of the symmetric cipher when authentication mechanism uses it. ## EXAMPLES ### Configuring amavis In the **amavisd.conf** file set protocol and amavis socket to: $protocol = "AM.PDP"; # Use AM.PDP protocol $unix_socketname = "$MYHOME/amavisd.sock"; # Listen on Unix socket ### $inet_socket_port = 10024; # Do not listen on TCP port Then (re)start the amavisd daemon. ### Configuring Postfix Add the following entries to Postfix **main.cf***: smtpd_milters = local: milter_connect_macros = j {client_name} {daemon_name} v milter_protocol = 6 Then (re)start the Postfix daemon. ### Configuring sendmail Add the following entries to file **sendmail.mc**: define(`confMILTER_MACROS_CONNECT', confMILTER_MACROS_CONNECT`, {client_resolve}') define(`confMILTER_MACROS_ENVFROM', confMILTER_MACROS_ENVFROM`, r, b') INPUT_MAIL_FILTER(`amavisd-milter', `S=local:, F=T, T=S:10m;R:10m;E:10m') Then rebuild **sendmail.cf** file, install it and (re)start the sendmail daemon. ### Running amavisd-milter This examples assumes that amavis is running as user *vscan*. The actual name is shown in the *$daemon_user* variable in **amavisd.conf**. ### Limiting maximum concurrent connections to amavisd To limit the maximum concurrent connections to amavis, run amavisd-milter with this options: su - vscan -c "amavisd-milter -m 4" ### Troubleshooting For troubleshooting, run amavisd-milter on the foreground and set the debug level to the appropriate value: su - vscan -c "amavisd-milter -f -d 4" Debug levels are: * 1 - Not errors but unexpected states (connection abort etc). * 2 - Main states in message processing. * 3 - All amavisd-milter debug messages. * 4-9 - Milter communication debugging (set **smfi_setdbg** to 1-6). ## SEE ALSO * https://github.com/prehor/amavisd-milter * https://www.ijs.si/software/amavisd/ * https://www.sendmail.org ## AUTHORS This manual page was written by Petr Rehor and is based on Jerzy Sakol initial work. ## BUGS Issues can be reported by using GitHub at: > https://github.com/prehor/amavisd-milter/issues Full detailed information on how to report issues, please see the Contribution Guidelines at: > https://github.com/prehor/amavisd-milter/blob/master/CONTRIBUTING.md Enhancements, requests and problem reports are welcome. If you run into problems, first check the GitHub issues before creating a new one. It is very likely that someone has encountered the same problem and it has already been solved. amavisd-milter-1.7.1/CHANGES000066400000000000000000000425131372525560500154500ustar00rootroot00000000000000This is the CHANGELOG for amavisd-milter. 20200906: amavisd-milter-1.7.1: Bug and compatibility fixies: - An empty sender must always be enclosed in angle brackets. 20190227: amavisd-milter-1.7.0: amavisd-milter has been moved from SourceForge to to GitHub. New features: - Fork after initializing milter socket. - Use client_name if available instead of hostname passed to xxfi_connect. - Generate amamvisd-milter.8 from AMAVISD-MILTER.md. Bug and compatibility fixies: - Fixed compiler warnings. - Converted indentation to spaces only. - Removed obsoleted file amavisd-milter.spec. 20190203: Generate amamvisd-milter.8 from AMAVISD-MILTER.md. 20190127: Spamassassin's RDNS_NONE check requires in the synthesized Received: header Forward-confirmed reverse DNS (FCrDNS) client name or "unknown" if the FCrDNS is not available. Sendmail sends the reverse DNS or IP address in square brackets both in the client_host argument and the {client_name} macro. If the client name is FCrDNS, the {client_reverse} macro is set to OK, otherwise to FORGED, TEMP or FAIL. Postfix sends a FCrDNS or "unknown" using the {client_name} macro. Thanks to: Florian Pritz 20190121: The milter socket is created before amavisd-milter is daemonized. This allows to return an error in case of impossibility to create the milter socket and package maintainers can adjust milter socket permissions immediatelly after the main process exits. Thanks to: Matus Uhlar 20190116: Removed obsoleted file amavisd-milter.spec. 20190106: amavisd-milter moved to GitHub. 20150525: Properly eliminate UTF8 characters in nroff output for man2html and README. 20150524: amavisd-milter-1.6.1: Bug and compatibility fixies: - Fixed bug when creating amavisd-new policy bank names. 20150524: Fixed bug when creating amavisd-new policy bank names. Thanks to: Christian Roessner 20130519: amavisd-milter-1.6.0: New features: - Added new amavisd-milter option -B which passes value of {daemon_name} milter macro as amavisd-new policy bank name. Bug and compatibility fixies: - Added amavisd-milter.spec for compilation with rpmbuild. - Fixed typo which prevents using LDFLAGS on Debian. - Fixed missing definition of true and false in libmilter/mfapi.h. 20130422: Added new amavisd-milter option -B which uses value of {daemon_name} milter macro as amavisd-new policy bank name. Thanks to: Andreas Schulze 20130125: Included amavisd-milter.spec for compilation with rpmbuild. Thanks to: Jo Rhett 20120727: Include stdbool.h before libmilter/mfapi.h in compat/compat.h because libmilter/mfapi.h not define true and false. 20101105: Fixed typo in aclocal/ax_path_milter.m4 which prevents using LDFLAGS on Debian. Thanks to: Harald Jenny 20100502: amavisd-milter-1.5.0: Bug and compatibility fixies: - Amavisd-new 2.7.0 introduce new AM.PDP response log_id. 20100501: Amavisd-new 2.7.0 introduce new AM.PDP response log_id. Thanks to: Andreas Schulze 20100131: amavisd-milter-1.4.1: New features: - Added new amavisd-milter option -P which causes that mails pass through when amavisd-new fails. Bug and compatibility fixies: - Debian installs libmilter.a to /usr/lib/libmilter. - sendmail 8.14 introduces three new libmilter callbacks. 20091029: amavisd-milter(8) manual page has been improved: - Added link to milter documentation. - Added socket name format description for sendmail and amavisd-new socket. 20091003: Added new amavisd-milter option -P which causes that mails pass through when amavisd-new fails. Thanks to: Uli Schellhaas 20090803: Bug fix - sendmail 8.14 introduces three new callbacks. 20090601: Compatibility fix - Debian installs libmilter.a to /usr/lib/libmilter. Thanks to: Simon Hobson 20081205: amavisd-milter-1.4.0: New features: - Added new amavisd-milter option -q which sets milter socket backlog used by listen(2). - Local state directory (default /var/amavis) is tunable via configure option: --localstatedir= - absolute path to local state directory. - Working directory (default local state directory) is tunable via configure option: --with-working-directory= - subdirectory of local state directory. --with-working-directory= - absolute path to working directory. Bug and compatibility fixies: - amavisd-milter convert CRLF to LF in mail body to support amavisd-new integrated DKIM signing and verification. - Fixed memory leak in fts_alloc() on Solaris. - Solaris doesn't have dirfd(). - Solaris 9 and older doesn't have sem_timedwait. 20081109: Local state directory (default /var/amavis) is tunable via configure option: --localstatedir= Working directory (default local state directory) is tunable via configure option: --with-working-directory= subdirectory of local state directory --with-working-directory= absolute path to working directory 20081108: Added new amavisd-milter option -q which sets milter socket backlog used by listen(2). 20081029: Compatibility fix - Solaris 9 and older doesn't have sem_timedwait. Use sem_trywait if sem_timedwait isn't available. Thans to: Henrik Krohns 20081029: Memory leak in fts_alloc() on Solaris fixed. Luke Mewburn wrote: In examining the code, it appears that the implementation of fts_alloc() can leak the struct stat fts_statp if you're not using ALIGNBYTES() && ALIGN(), which I guess that Solaris doesn't have. Given that the leaked buffers are 160 bytes in size (184960/1156), I suspect that's sizeof(struct stat) and our culprit. Thanks to: Henrik Krohns Submitted by: Luke Mewburn 20081028: Compatibility fix - Solaris doesn't have dirfd(). Thanks to: Henrik Krohns 20080701: Sync fts_open.c with the NetBSD libc (upstream) fts.c, which brought in a few fixes, including one that looked like a potential memory leak. Thanks to: Luke Mewburn Properly check -D option parameters. Thanks to: Henrik K 20080310: Convert CRLF to LF in mail body, because sendmail send always CRLF, but amavisd-new requires that a message on a temporary file is in a native text format of a host's file system, i.e. with LF terminated lines on Unix or Linux. Thanks to: David Schweikert and Mark Martinec 20070923: amavisd-milter-1.3.1 Bug fixies: - Properly calculate timezone offset for synthesized Received header on platforms where gmtime() and localtime() returns the pointer to the same static struct. 20070923: Better explained -D option. 20070915: In mlfi.c:mlfi_envfrom() the gmtime() and localtime() call returns the pointer to the same static struct. Thus pointers gt and lt are identical and the UTC offset is always +0000 which is bogus. Thanks to: Curtis Doty 20070902: amavisd-milter-1.3.0 New features: - Implemented AM.PDP request attribute policy_bank (amavisd-new 2.5.0 or higher is required). Currently SMTP_AUTH, SMTP_AUTH_ and SMTP_AUTH_ policy bank names are sent when the remote client is authenticated. - Added new amavisd-milter option -D client|server which allow personalized header or body modifications. Bug fixies: - fixed bug when amavisd communication buffer is growing. Compatibility: - properly get queue id when amavisd-milter is used with postfix. Performance: - improved performance under heavy load. 20070901: Performance optimization: use sem_timedwait instead of sem_trywait+sleep(1) for amavisd connection lock. Requested by: David Schweikert 20070831: Added new amavisd-milter option -D client|server which allow personalized header or body modifications. Requested by: Brian C. Huffman Fixed bug when amavisd communication buffer is growing. Thanks to: Michael Moeller 20070729: Better compatibility with Postfix: it does give information about the queue-number only after the RCPT-TO-phase. Thanks to: David Schweikert 20061219: amavisd-milter-1.2.1 New features: - Implemented AM.PDP request attribute 'protocol_name'. Add folowing line to your sendmail.mc: define(`confMILTER_MACROS_ENVFROM', confMILTER_MACROS_ENVFROM`, r, b') Limited support for sendmail 8.12: - smfi_addheader() is used instead of smfi_insheader() for insheader and addheader AM.PDP responses. This works well with amavisd-new 2.4.3 or newer. - smfi_progress() isn't called when amavisd-milter wait for amavisd-new communication socket. - AM.PDP response quarantine isn't implemented. Bug fixies: - add synthesized Received header for Spamassassin. - properly handle IPv6 client address. 20061217: Added support for IPv6 addresses. Thanks to: Mark Martinec 20061206: Limited support for legacy sendmail 8.12: - AM.PDP v2 response attribute quarantine isn't implemented. Thanks to: Adam Gibson 20061203: Added info about user authentication to synthesized received header. Format of synthesized received header is now: Received: from ( []) (authenticated bits=) by ( []) with (authenticated as ) id ; (envelope-from ) Thanks to: Jo Rhett 20061113: Limited support for legacy sendmail 8.12: - amavisd-milter don't call smfi_progress when wait for amavisd-new communication socket. - amavisd-milter use smfi_addheader instead of smfi_insheader for insheader and addheader AM.PDP response attributes. This work well with amavisd-new 2.4.3 or newer. Older versions are affected with bug fixed on 20060714. 20061017: Implemented AM.PDP request attribute protocol_name. amavisd-milter adds Received header for the current connection to the mail file as the first line because without the most recent Received header, Spamassassin reports lots of false hits on rules checking where the message was received from. Thanks to: Jo Rhett NOTE: Please add these line to your sendmail.mc: define(`confMILTER_MACROS_ENVFROM', confMILTER_MACROS_ENVFROM`, r, b') 20061008: amavisd-milter-1.2.0 Improved amavisd connection locking. When amavisd connection is closed, lock is removed immediately. 20061004: Implemented AM.PDP protocol version 2. New response attributes are version_server, insheader and quarantine. Tru64 portability fix: don't use variadic macros. 20060714: Fixed bug with addheader: amavisd-new assume that header is added to the end of headers but sendmail insert it before first occurence of the header with same name. Submitted by: Krzysztof Oledzki 20060616: Amavisd communication buffer is now dynamically allocated. MAXAMABUF is maximum buffer length, AMABUFCHUNK is initial length and reallocation step. Buffer is allocated for every connection. Tru64 portability fix: replace missing daemon(8) function. 20060414: amavisd-milter-1.1.2 MAXAMABUF is increased from 2048 to 65536 because it must be greater then confMAX_HEADERS_LENGTH which is usually set to 32768. If mail header is longer than MAXAMABUF, mail is permanently rejected with temporary fail return code and will never reach the recipents. Added new configure option --with-sendmail= which change base directory in which are placed libmilter header files and library (e.q. /usr/local on FreeBSD when you using sendmail from ports). 20060125: amavisd-milter-1.1.1 Portability release. amavisd-milter-1.1 now can be compiled on FreeBSD, NetBSD, OpenBSD, Linux and Solaris. Please report success or problems on the users mailing list. 20060125: Linux and Solaris portability fix: sem_t is declared as pointer on *BSD but as structure on Linux and Solaris. Solaris portability fix: search sem_init in librt. Solaris portability fix: replace missing fts.h API. 20060124: amavisd-milter-1.1.0 New features: - amavisd-milter can limit number of concurrent connections to amavisd-new - amavisd-milter is responsible to remove message work directory 20060124: README is now generated from amavisd-milter(8) manual page. Remove all files and directories in working directory because amavisd-new sometimes don't do it (e.q. when antivirus fail). 20060123: Limit maximum concurrent connections to amavisd: amavisd-milter -m 4 -M 600 means that there is limit to maximum 4 concurrent connections to amavisd and wait maximum 10 minutes (10*60 sec) for connection to amavisd. When connection isn't available, amavisd-milter sleep for 1 sec. Every minute of waiting is sendmail triggered via smfi_progress. When time limit run out then amvisd-milter return temporary fail. 20060113: amavisd-milter-1.0.4 Bugfix release. Added manual page amavisd-milter(8). INCOMPATIBILITY with 1.0.3 and previous versions: Default working directory was changed from /var/amavis/tmp to /var/amavis to accordance with amavisd-new $helpers_home default value. To go back to the previous behaviour run amavisd-milter with option -w /var/amavis/tmp 20060109: When stopping amavisd-new one (maybe not always just one) of milter threads amavisd-milter start to eat up a cpu time. Submitted by: Grzegorz Piszczek Fixed bug in mlfi_close(): amavisd-milter dies randomly due to segmentation fault. Submitted by: Grzegorz Piszczek 20051225: Added amavisd-milter(8) manual page. README and website are synchronized with this manual page. Changed default working directory to /var/amavis (default value for amavisd-new helpers home directory). 20051204: Some users reports problem with ENOMEM in amavis_response(). Consequently MAXAMABUF is increased from 1024 to 2048, amavis_request() and amavis_response() returns EOVERFLOW when buffer size isn't enough and truncated response is logged when debug level is >= 3. 20051202: Fixed default path to pidfile in README and website. Submitted by: Jerzy Sakol 20050829: Added amavisd-new configuration section to README. 20050701: amavisd-milter-1.0.3 Bugfix release. Properly set amavisd connection timeout. 20050701: Set amavisd_timeout instead of mlfi_timeout for -T option. Submitted by: Mr.Sompis Junsui 20050701: Unlink old pid file on start. 20050605: amavisd-milter-1.0.2 Bugix release. Fix memory leak in mlfi_cleanup. 20050529: amavisd-milter-1.0.1 Portability release. Now can be compiled on Debian 3.0, Fedora FC2 and FC3, OpenBSD 3.4 and Solaris 9. Tested on Sourceforge compile farm. Please report success or problems on the users mailing list: http://lists.sourceforge.net/lists/listinfo/amavisd-milter-users 20050522: Define EILSEQ on systems without it. 20050520: mkdtemp(3) compatibility function. 20050512: Initial version of README. 20050512: Better portability with autoconf macros from http://ac-archive.sourceforge.net/ 20050510: strlcpy(3) compatibility function. 20050511: amavisd-milter-1.0.0 First public release. Fully tested on FreeBSD 5.3. amavisd-milter-1.7.1/CONTRIBUTING.md000066400000000000000000000067261372525560500167140ustar00rootroot00000000000000# Contribution Guidelines We're glad you want to contribute to the project! This document will help answer common questions you may have during your first contribution. ## Submitting Issues Not every contribution comes in the form of code. Submitting, confirming, and triaging issues is an important task for any project. We use GitHub to track all project issues. ## Contribution Process We have a 3 step process for contributions: 1. Fork the project on GitHub. 2. Create a feature branch for your change. 3. Commit changes to a feature branch, making sure to sign-off those changes for the [Developer Certificate of Origin](#developer-certification-of-origin-dco). 4. Push your feature branch to GitHub and open a pull request against master. ## Developer Certification of Origin (DCO) Licensing is very important to open source projects. It helps ensure the software continues to be available under the terms that the author desired. This project uses [the BSD-3-Clause](LICENSE) to strike a balance between open contribution and allowing you to use the software however you would like to. The license tells you what rights you have that are provided by the copyright holder. It is important that the contributor fully understands what rights they are licensing and agrees to them. Sometimes the copyright holder isn't the contributor, such as when the contributor is doing work on behalf of a company. To make a good faith effort to ensure these criteria are met, this project requires the Developer Certificate of Origin (DCO) process to be followed. The DCO is an attestation attached to every contribution made by every developer. In the commit message of the contribution, the developer simply adds a Signed-off-by statement and thereby agrees to the DCO, which you can find below or at . ``` Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` The DCO requires a sign-off message in the following format appear on each commit in the pull request: ``` Signed-off-by: First Last ``` The DCO text can either be manually added to your commit body, or you can add either **-s** or **--signoff** to your usual git commit commands. If you forget to add the sign-off you can also amend a previous commit with the sign-off by running **git commit --amend -s**. If you've pushed your changes to GitHub already you'll need to force push your branch after this with **git push -f**. amavisd-milter-1.7.1/INSTALL000066400000000000000000000152611372525560500155060ustar00rootroot00000000000000Obtaining the software ====================== Fetch the tarball from GitHub and unpack it: https://github.com/prehor/amavisd-milter/releases Start reading with README.md, then CHANGES and INSTALL. Basic Installation ================== The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, a file `config.cache' that saves the results of its tests to speed up reconfiguring, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.in' is used to create `configure' by a program called `autoconf'. You only need `configure.in' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes a while. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Type `make install' to install the programs and any data files and documentation. 4. You can remove the program binaries and object files from the source code directory by typing `make clean'. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. You can give `configure' initial values for variables by setting them in the environment. Using a Bourne-compatible shell, you can do that on the command line like this: CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure Or on systems that have the `env' program, you can do it like this: env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not supports the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PATH', the package will use PATH as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. Specifying the System Type ========================== There may be some features `configure' can not figure out automatically, but needs to determine by the type of host the package will run on. Usually `configure' can figure that out, but if it prints a message saying it can not guess the host type, give it the `--host=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name with three fields: CPU-COMPANY-SYSTEM See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the host type. If you are building compiler tools for cross-compiling, you can also use the `--target=TYPE' option to select the type of system they will produce code for and the `--build=TYPE' option to select the type of system on which you are compiling the package. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Operation Controls ================== `configure' recognizes the following options to control how it operates. `--cache-file=FILE' Use and save the results of the tests in FILE instead of `./config.cache'. Set FILE to `/dev/null' to disable caching, for debugging `configure'. `--help' Print a summary of the options to `configure', and exit. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--version' Print the version of Autoconf used to generate the `configure' script, and exit. `configure' also accepts some other, not widely useful, options. amavisd-milter-1.7.1/LICENSE000066400000000000000000000030101372525560500154470ustar00rootroot00000000000000BSD 3-Clause License Copyright (c) 2005-2019, Petr Řehoř, https://github.com/prehor All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. amavisd-milter-1.7.1/Makefile.am000066400000000000000000000013751372525560500165120ustar00rootroot00000000000000# Subdirectories SUBDIRS= \ compat \ amavisd-milter DIST_SUBDIRS= \ ${SUBDIRS} # Not a GNU package AUTOMAKE_OPTIONS= \ foreign # Distribution files EXTRA_DIST= \ .editorconfig \ AMAVISD-MILTER.md \ CHANGES \ INSTALL \ LICENSE \ Makefile.am \ Makefile.in \ aclocal.m4 \ autoconf.sh \ config.h.in \ configure \ configure.ac dist-hook: cd amavisd-milter; \ $(MAKE) amavisd-milter.8 maintainer-clean-local: -rm -f Makefile.in -rm -f aclocal/ar-lib -rm -f aclocal/compile -rm -f aclocal/config.guess -rm -f aclocal/config.sub -rm -f aclocal/depcomp -rm -f aclocal/install-sh -rm -f aclocal/missing -rm -f aclocal/mkinstalldirs -rm -f aclocal.m4 -rm -f amavisd-milter-*.tar.gz -rm -f config.h.in -rm -f config.h.in~ -rm -f configure amavisd-milter-1.7.1/README.md000066400000000000000000000032111372525560500157240ustar00rootroot00000000000000# amavisd-milter [![CircleCI Status Badge](https://circleci.com/gh/prehor/amavisd-milter.svg?style=shield&circle-token=e3c02b685cb35b8a4428e988d7874790a9409fe9)](https://circleci.com/gh/prehor/amavisd-milter) amavisd-milter is a milter interface for the [amavis](https://www.amavis.org) spam filter engine. ## Installing The simplest way to compile amavisd-milter: ``` curl -L https://github.com/prehor/amavisd-milter/releases/download/1.7.1/amavisd-milter-1.7.1.tar.gz | tar xfz - cd amavisd-milter-1.7.1 ./configure make all make install make clean ``` For more information on installation, see [INSTALL](INSTALL). ## Usage A complete description of usage, configuration and troubleshooting is at [amavisd-milter(8) manual page](AMAVISD-MILTER.md). ## Reporting Issues Issues can be reported by using [GitHub Issues](/../../issues). Full details on how to report issues can be found in the [Contribution Guidelines](CONTRIBUTING.md). ## Contributing Please read the [Contribution Guidelines](CONTRIBUTING.md), and ensure you are signing all your commits with [DCO sign-off](CONTRIBUTING.md#developer-certification-of-origin-dco). ## Versioning We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](/../../tags). ## Authors * [Petr Řehoř](https://github.com/prehor) - Initial work. See also the list of [contributors](https://github.com/prehor/amavisd-milter/contributors) who participated in this project. For contributors up to amavisd-milter-1.6.1 see [CHANGELOG](CHANGES). ## License This project is licensed under the BSD-3-Clause License - see the [LICENSE](LICENSE) file for details. amavisd-milter-1.7.1/aclocal/000077500000000000000000000000001372525560500160465ustar00rootroot00000000000000amavisd-milter-1.7.1/aclocal/.gitignore000066400000000000000000000001021372525560500200270ustar00rootroot00000000000000ar-lib compile config.guess config.sub depcomp install-sh missing amavisd-milter-1.7.1/aclocal/ac_cpp_func.m4000066400000000000000000000016701372525560500205540ustar00rootroot00000000000000# EXPAT ORIGINAL: http://expat.cvs.sourceforge.net/expat/expat/configure.in # AC_CPP_FUNC # ------------------ # # Checks to see if ANSI C99 CPP variable __func__ works. # If not, perhaps __FUNCTION__ works instead. # If not, we'll just define __func__ to "". AC_DEFUN([AC_CPP_FUNC], [AC_REQUIRE([AC_PROG_CC_STDC])dnl AC_CACHE_CHECK([for an ANSI C99-conforming __func__], ac_cv_cpp_func, [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [[char *foo = __func__;]])], [ac_cv_cpp_func=yes], [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [[char *foo = __FUNCTION__;]])], [ac_cv_cpp_func=__FUNCTION__], [ac_cv_cpp_func=no])])]) if test $ac_cv_cpp_func = __FUNCTION__; then AC_DEFINE(__func__,__FUNCTION__, [Define to __FUNCTION__ or "" if `__func__' does not conform to ANSI C.]) elif test $ac_cv_cpp_func = no; then AC_DEFINE(__func__,"__FILE__:__LINE__", [Define to __FUNCTION__ or "" if `__func__' does not conform to ANSI C.]) fi ])# AC_CPP_FUNC amavisd-milter-1.7.1/aclocal/acinclude.m4000066400000000000000000000101071372525560500202360ustar00rootroot00000000000000dnl Enables debug output and debug symbols AC_DEFUN([ACX_ENABLE_DEBUG], [ dnl Configure --enable-debug option AC_ARG_ENABLE(debug,[ --enable-debug enables debug output and debug symbols @<:@default=no@:>@], [ if test $enableval = "no" then enable_debug="no" else enable_debug="yes" fi ], [ enable_debug="no" ]) dnl This prevents stupid AC_PROG_CC to add "-g" to the default CFLAGS CFLAGS=" $CFLAGS" AC_REQUIRE([AC_PROG_CC]) if test "$GCC" = "yes"; then if test "$enable_debug" = "yes" then CFLAGS="-g -O2 $CFLAGS" CFLAGS="-W -Wall -ansi -pedantic -Wbad-function-cast -Wcast-align $CFLAGS" CFLAGS="-Wcast-qual -Wchar-subscripts -Winline -Wmissing-prototypes $CFLAGS" CFLAGS="-Wmissing-declarations -Wnested-externs -Wpointer-arith $CFLAGS" CFLAGS="-Wredundant-decls -Wshadow -Wstrict-prototypes -Wwrite-strings $CFLAGS" else CFLAGS="-O2 -DNDEBUG $CFLAGS" fi fi AC_LANG_C ]) dnl Set local state directory AC_DEFUN([AC_LOCAL_STATE_DIR], [ if test "$localstatedir" = '${prefix}/var'; then AC_SUBST([localstatedir], ['/var/amavis']) fi test "x$prefix" = xNONE && prefix_NONE=yes && prefix="$ac_default_prefix" test "x$exec_prefix" = xNONE && exec_prefix_NONE=yes && exec_prefix="${prefix}" eval LOCALSTATEDIR=\""$localstatedir"\" dnl In Autoconf 2.60, ${datadir} refers to ${datarootdir}, which in turn dnl refers to ${prefix}. Thus we have to use `eval' twice. eval LOCALSTATEDIR=\""$LOCALSTATEDIR"\" AH_TEMPLATE([LOCAL_STATE_DIR], [Default location to store state files.]) AC_DEFINE_UNQUOTED([LOCAL_STATE_DIR], ["$LOCALSTATEDIR"]) test "$prefix_NONE" && prefix=NONE test "$exec_prefix_NONE" && exec_prefix=NONE ]) dnl Set working dir AC_DEFUN([AC_WORKING_DIR], [ AH_TEMPLATE([WORKING_DIR], [Default location of working directory.]) AC_ARG_WITH([working-dir], [ --with-working-dir@<:@=@:>@ set working directory (default tmp) SUBDIR subdirectry of local state dirertory /DIR absolute path to working directory], [ case "$with_working_dir" in no) AC_DEFINE_UNQUOTED([WORKING_DIR], [LOCAL_STATE_DIR]);; yes) AC_DEFINE_UNQUOTED([WORKING_DIR], [LOCAL_STATE_DIR] "/tmp");; /*) AC_DEFINE_UNQUOTED([WORKING_DIR], ["$with_working_dir"]);; *) AC_DEFINE_UNQUOTED([WORKING_DIR], [LOCAL_STATE_DIR "/$with_working_dir"]);; esac ], [AC_DEFINE_UNQUOTED([WORKING_DIR], [LOCAL_STATE_DIR]) ]) ]) dnl Checks for working POSIX semaphores AC_DEFUN([AC_CHECK_POSIX_SEMAPHORES], [ AC_MSG_CHECKING(if POSIX semaphores are working) AC_TRY_RUN([ #include int main() { sem_t sem; int rc; rc = sem_init(&sem, 0, 0); return rc; }], AC_MSG_RESULT(yes), AC_MSG_ERROR(no), AC_MSG_ERROR(no) ) ]) dnl Checks for d_namlen in struct dirent AC_DEFUN([AC_STRUCT_DIRENT_D_NAMLEN], [ AC_MSG_CHECKING(for d_namlen in struct dirent) AC_TRY_COMPILE([ #if HAVE_DIRENT_H # include #else # define dirent direct # if HAVE_SYS_NDIR_H # include # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # endif #endif ], [ struct dirent dp; int X = dp.d_namlen; ], AC_DEFINE([HAVE_STRUCT_DIRENT_D_NAMLEN], 1, [Define to 1 if `struct direct' has a d_namlen element]) AC_MSG_RESULT(yes), AC_MSG_RESULT(no) ) ]) dnl Checks for AF_INET6 AC_DEFUN([AC_CHECK_AF_INET6], [ AC_CHECK_DECLS([AF_INET6],[],[],[ #include #include ]) ]) dnl Checks for INET6_ADDRSTRLEN AC_DEFUN([AC_CHECK_INET6_ADDRSTRLEN], [ AC_CHECK_DECLS([INET6_ADDRSTRLEN],[],[],[ #include #include #include ]) ]) dnl Checks for struct sockaddr_in6 AC_DEFUN([AC_CHECK_STRUCT_SOCKADDR_IN6], [ AC_CHECK_TYPES([struct sockaddr_in6],[],[],[ #include #include #include ]) ]) amavisd-milter-1.7.1/aclocal/acx_pthread.m4000066400000000000000000000317071372525560500206020ustar00rootroot00000000000000dnl http://avahi.org/log/trunk/common/acx_pthread.m4 dnl cahngeset 1277 dnl dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) dnl dnl @summary figure out how to build C programs using POSIX threads dnl dnl This macro figures out how to build C programs using POSIX threads. dnl It sets the PTHREAD_LIBS output variable to the threads library and dnl linker flags, and the PTHREAD_CFLAGS output variable to any special dnl C compiler flags that are needed. (The user can also force certain dnl compiler flags/libs to be tested by setting these environment dnl variables.) dnl dnl Also sets PTHREAD_CC to any special C compiler that is needed for dnl multi-threaded programs (defaults to the value of CC otherwise). dnl (This is necessary on AIX to use the special cc_r compiler alias.) dnl dnl NOTE: You are assumed to not only compile your program with these dnl flags, but also link it with them as well. e.g. you should link dnl with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS dnl $LIBS dnl dnl If you are only building threads programs, you may wish to use dnl these variables in your default LIBS, CFLAGS, and CC: dnl dnl LIBS="$PTHREAD_LIBS $LIBS" dnl CFLAGS="$CFLAGS $PTHREAD_CFLAGS" dnl CC="$PTHREAD_CC" dnl dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to dnl that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). dnl dnl ACTION-IF-FOUND is a list of shell commands to run if a threads dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands to dnl run it if it is not found. If ACTION-IF-FOUND is not specified, the dnl default action will define HAVE_PTHREAD. dnl dnl Please let the authors know if this macro fails on any platform, or dnl if you have any other suggestions or comments. This macro was based dnl on work by SGJ on autoconf scripts for FFTW (www.fftw.org) (with dnl help from M. Frigo), as well as ac_pthread and hb_pthread macros dnl posted by Alejandro Forero Cuervo to the autoconf macro repository. dnl We are also grateful for the helpful feedback of numerous users. dnl dnl @category InstalledPackages dnl @author Steven G. Johnson dnl @version 2006-05-29 dnl @license GPLWithACException dnl dnl Checks for GCC shared/pthread inconsistency based on work by dnl Marcin Owsiany AC_DEFUN([ACX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_LANG_SAVE AC_LANG_C acx_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on True64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) AC_MSG_RESULT($acx_pthread_ok) if test x"$acx_pthread_ok" = xno; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case "${host_cpu}-${host_os}" in *solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthreads/-mt/ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags" ;; esac if test x"$acx_pthread_ok" = xno; then for flag in $acx_pthread_flags; do case $flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -*) AC_MSG_CHECKING([whether pthreads work with $flag]) PTHREAD_CFLAGS="$flag" ;; pthread-config) AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no) if test x"$acx_pthread_config" = xno; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$flag]) PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_TRY_LINK([#include ], [pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], [acx_pthread_ok=yes]) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" AC_MSG_RESULT($acx_pthread_ok) if test "x$acx_pthread_ok" = xyes; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$acx_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_MSG_CHECKING([for joinable pthread attribute]) attr_name=unknown for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_TRY_LINK([#include ], [int attr=$attr; return attr;], [attr_name=$attr; break]) done AC_MSG_RESULT($attr_name) if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, [Define to necessary symbol if this constant uses a non-standard name on your system.]) fi AC_MSG_CHECKING([if more special flags are required for pthreads]) flag=no case "${host_cpu}-${host_os}" in *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; esac AC_MSG_RESULT(${flag}) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: must compile with xlc_r or cc_r if test x"$GCC" != xyes; then AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) else PTHREAD_CC=$CC fi # The next part tries to detect GCC inconsistency with -shared on some # architectures and systems. The problem is that in certain # configurations, when -shared is specified, GCC "forgets" to # internally use various flags which are still necessary. AC_MSG_CHECKING([whether to check for GCC pthread/shared inconsistencies]) check_inconsistencies=yes case "${host_cpu}-${host_os}" in *-darwin*) check_inconsistencies=no ;; esac if test x"$GCC" != xyes -o "x$check_inconsistencies" != xyes ; then AC_MSG_RESULT([no]) else AC_MSG_RESULT([yes]) # In order not to create several levels of indentation, we test # the value of "$ok" until we find out the cure or run out of # ideas. ok="no" # # Prepare the flags # save_CFLAGS="$CFLAGS" save_LIBS="$LIBS" save_CC="$CC" # Try with the flags determined by the earlier checks. # # -Wl,-z,defs forces link-time symbol resolution, so that the # linking checks with -shared actually have any value # # FIXME: -fPIC is required for -shared on many architectures, # so we specify it here, but the right way would probably be to # properly detect whether it is actually required. CFLAGS="-shared -fPIC -Wl,-z,defs $CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CC="$PTHREAD_CC" AC_MSG_CHECKING([whether -pthread is sufficient with -shared]) AC_TRY_LINK([#include ], [pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], [ok=yes]) if test "x$ok" = xyes; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi # # Linux gcc on some architectures such as mips/mipsel forgets # about -lpthread # if test x"$ok" = xno; then AC_MSG_CHECKING([whether -lpthread fixes that]) LIBS="-lpthread $PTHREAD_LIBS $save_LIBS" AC_TRY_LINK([#include ], [pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], [ok=yes]) if test "x$ok" = xyes; then AC_MSG_RESULT([yes]) PTHREAD_LIBS="-lpthread $PTHREAD_LIBS" else AC_MSG_RESULT([no]) fi fi # # FreeBSD 4.10 gcc forgets to use -lc_r instead of -lc # if test x"$ok" = xno; then AC_MSG_CHECKING([whether -lc_r fixes that]) LIBS="-lc_r $PTHREAD_LIBS $save_LIBS" AC_TRY_LINK([#include ], [pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], [ok=yes]) if test "x$ok" = xyes; then AC_MSG_RESULT([yes]) PTHREAD_LIBS="-lc_r $PTHREAD_LIBS" else AC_MSG_RESULT([no]) fi fi if test x"$ok" = xno; then # OK, we have run out of ideas AC_MSG_WARN([Impossible to determine how to use pthreads with shared libraries]) # so it's not safe to assume that we may use pthreads acx_pthread_ok=no fi CFLAGS="$save_CFLAGS" LIBS="$save_LIBS" CC="$save_CC" fi else PTHREAD_CC="$CC" fi AC_SUBST(PTHREAD_LIBS) AC_SUBST(PTHREAD_CFLAGS) AC_SUBST(PTHREAD_CC) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$acx_pthread_ok" = xyes; then ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) : else acx_pthread_ok=no $2 fi AC_LANG_RESTORE ])dnl ACX_PTHREAD amavisd-milter-1.7.1/aclocal/ax_path_milter.m4000066400000000000000000000346301372525560500213160ustar00rootroot00000000000000dnl @synopsis AX_PATH_MILTER([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) dnl dnl This macro tries to automatically find the library libmilter.a and dnl the header file "libmilter/mfapi.h", which are required when compiling dnl a milter for Sendmail. When successful, it sets the output variable dnl MILTER_LIBS to "-lmilter", MILTER_LDFLAGS to contain an -Lpathtolib dnl option, and MILTER_CPPFLAGS to contain an -Ipathtoinclude option, if dnl they are necessary. dnl dnl The easiest way to use this macro is something like: dnl dnl AX_PATH_MILTER([8.12],[ dnl LIBS="$MILTER_LIBS $LIBS" dnl LDFLAGS="$MILTER_LDFLAGS $LDFLAGS" dnl CPPFLAGS="$CPPFLAGS $MILTER_CPPFLAGS" dnl ],[ dnl AC_MSG_ERROR([required milter library and header not found]) dnl ]) dnl dnl If the macro is successful, it just adds any flags to the necessary dnl environment. If it is not successful, it would likely be a fatal dnl error, because if an application is linking with libmilter.a, dnl it is probably because it is a milter. dnl dnl There are two optional "--with" options for configure which are added. dnl If they are specified, they override any searching that is done. dnl They are: dnl dnl --with-sendmail-base= This option is used to explicitly dnl specify the base of the sendmail distribution. dnl dnl --with-sendmail-obj= The option is used to explicitly specify dnl the "obj.*" subdirectory in the sendmail distribution dnl that should be used. dnl dnl When sendmail-base is not specified, the current environment is first dnl tested to see if the header and library are available, and if so dnl MILTER_LDFLAGS and MILTER_CPPFLAGS are left empty. dnl dnl There are two places that are searched for the sendmail base dnl directory. The first location is one directory down from the dnl current directory. It checks if there is a directory of the dnl form sendmail-8.1*, limited to version 8.12.x or higher, then dnl chooses the directory with the highest version number. dnl If that method does not succeed, it then looks in the file dnl /etc/mail/sendmail.cf for the directory it was built from, dnl and uses the base of that distribution. If neither of these dnl methods work, then it fails. dnl dnl There are two methods for finding the "obj.*" directory when it is dnl not specified. The first is to try to run sendmail's Build program dnl with the -M option which will print out the name of the obj. directory dnl for the tool in the directory where it is run from. If this dnl does not work, is looks for the newest directory of the dnl form "obj.*" in the sendmail base directory. dnl dnl Two addition output variables that are defined, whether or not the dnl files are found are SENDMAIL_BASE_DIR and SENDMAIL_OBJ_DIR, which dnl are the suspected location of the sendmail base directory and dnl obj.* subdirectory. dnl dnl NOTE: POSIX threads MUST be configured BEFORE this function is called dnl or it will not find libmilter.a even if it exists. The easiest way is dnl to use the ACX_PTHREAD macro by Steven G. Johnson and Alejandro Forero dnl Cuervo which is available from the Autoconf Macro Archive. dnl dnl @author Tim Toolan dnl ############################################################################### AC_DEFUN([AX_PATH_MILTER], [ # Used to indicate success or failure of this function. ax_path_milter_ok=no # Convert sections of MINIMUM-VERSION to three digit numbers by adding zeros. # For example 8.12.9 would become 008.012.009 ac_milter_minimum_version=`echo "$1" | sed 's,\([[0-9]]*\),x\1x,g;s,x\([[0-9]]\)x,x0\1x,g;s,x\([[0-9]][[0-9]]\)x,x0\1x,g;s,x,,g'` # Add options --with-sendmail --with-sendmail-base and --with-sendmail-obj # to configure. AC_ARG_WITH([sendmail], [ --with-sendmail= base directory of sendmail installation]) AC_ARG_WITH([sendmail-base], [ --with-sendmail-base= base directory of sendmail distribution]) AC_ARG_WITH([sendmail-obj], [ --with-sendmail-obj= obj.* subdirectory in sendmail distribution]) # Check for functions required by libmilter. AC_CHECK_FUNC(inet_aton, [], [AC_SEARCH_LIBS(inet_aton, [socket nsl resolv])]) AC_CHECK_FUNC(socket, [], [AC_SEARCH_LIBS(socket, [socket nsl])]) AC_CHECK_FUNC(gethostbyname, [], [AC_SEARCH_LIBS(gethostbyname, [socket nsl])]) # Check if the linker accepts --rpath (for Darwin) AC_MSG_CHECKING([if ld accepts --rpath]) SAVEDLDFLAGS=$LDFLAGS LDFLAGS=$LDFLAGS" -Wl,--rpath=/" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [rpath="--rpath="; ldrpath=yes], [rpath="-L"; ldrpath=no]) LDFLAGS=$SAVEDLDFLAGS AC_MSG_RESULT([$ldrpath]) ############################################################################### # # If neither --with-sendmail-base or --with-sendmail-obj is specified # check the existing environment first for mfapi.h and libmilter without # modifying CPPFLAGS, LDFLAGS, and LIBS first. # if test "x$with_sendmail_base$with_sendmail_obj" = "x" ; then if test "x$with_sendmail" != "x" ; then CPPFLAGS="$CPPFLAGS -I$with_sendmail/include" LDFLAGS="$LDFLAGS -L$with_sendmail/lib -Wl,$rpath$with_sendmail/lib" AC_MSG_CHECKING([for sendmail install directory]) AC_MSG_RESULT([$with_sendmail]) else AC_MSG_CHECKING([for sendmail install directory]) AC_MSG_RESULT([default]) fi AC_CHECK_HEADER([libmilter/mfapi.h],[ AC_CHECK_LIB([milter],[smfi_main],[ # both tests succeeded so indicate success ax_path_milter_ok=yes # add -lmilter to the libraries to link MILTER_LIBS="-lmilter" ]) ]) # Debian use for libmilter.a /usr/lib/libmilter instead of /usr/lib if test "$ax_path_milter_ok" = "no" ; then unset ac_cv_lib_milter_smfi_main ac_milter_save_LDFLAGS="$LDFLAGS" MILTER_LDFLAGS="-L/usr/lib/libmilter" LDFLAGS="$MILTER_LDFLAGS $LDFLAGS" AC_CHECK_HEADER([libmilter/mfapi.h],[ AC_CHECK_LIB([milter],[smfi_main],[ # both tests succeeded so indicate success ax_path_milter_ok=yes # add -lmilter to the libraries to link MILTER_LIBS="-lmilter" ]) ]) LDFLAGS="$ac_milter_save_LDFLAGS" fi if test "$ax_path_milter_ok" = "no" ; then # Unset the cached test results because we will be trying them again later. ac_milter_tmp=abcdefg if unset ac_milter_tmp 2> /dev/null ; then unset ac_cv_header_libmilter_mfapi_h unset ac_cv_lib_milter_smfi_main else AC_MSG_WARN( [system doesn't have unset so either use --with-sendmail-base or set LDFLAGS and CPPFLAGS with the necessary -L and -I options]) fi fi fi ############################################################################### # # If didn't already fine necessary files then search. # if test "$ax_path_milter_ok" = "no" ; then ############################################################################# # # Determine the sendmail base directory and set SENDMAIL_BASE_DIR. # if test "x$with_sendmail_base" != "x" ; then AC_MSG_CHECKING([for sendmail base directory]) # set SENDMAIL_BASE_DIR to the one specified by--with-sendmail-base SENDMAIL_BASE_DIR="$with_sendmail_base" AC_MSG_RESULT([$SENDMAIL_BASE_DIR]) else AC_MSG_CHECKING([for sendmail base directory in ../ ]) # # --with-sendmail-base is not used, so we will try to determine it # # 1) List all directories one level down that look like sendmail. # 2) Select ones that are sendmail 8.12 or higher (including 8.13 # versions when they come out). # 3) Replace any single digit last version numbers with a two digit # version number (ie. 8.12.9 becomes 8.12.09). # 4) Sort all of the directories found in reverse order. # 5) Take the first one (the highest version). # 6) Restore the single digit version numbers. # ac_milter_tmp=`ls -d ../sendmail-8.1* 2> /dev/null | grep '../sendmail-8.1[[2-9]]' | sed 's,\.\([[0-9]]\)$,.0\1,' | sort -r | sed '1q' | sed 's,\.0\([[0-9]]\)$,.\1,'` # Convert found version sections to three digit numbers by adding zeros. ac_milter_found_version=`echo "$ac_milter_tmp" | sed 's,.*/sendmail-,,;s,\([[0-9]]*\),x\1x,g;s,x\([[0-9]]\)x,x0\1x,g;s,x\([[0-9]][[0-9]]\)x,x0\1x,g;s,x,,g'` # If ac_milter_minimum_version is equal to ac_milter_lower_version, then # the found version is greater than or equal to the minumum version. # Pick the version string that is the lesser of the two. # An empty string would be less than anything. # In short, ac_milter_version_ok will equal yes if the version is ok, # and no otherwise. ac_milter_version_ok=`echo "x$ac_milter_minimum_version x$ac_milter_found_version" | sort | sed '1q' | sed "s,x${ac_milter_minimum_version},yes,;s,x${ac_milter_found_version},no," ` # If we have something add the current directory to it. if test "x$ac_milter_tmp" != "x" ; then ac_milter_tmp="`pwd`/$ac_milter_tmp" fi if test -r "${ac_milter_tmp}/include/libmilter/mfapi.h" && \ test "$ac_milter_version_ok" = "yes" ; then # The file mfapi.h exists so we will use this as SENDMAIL_BASE_DIR. SENDMAIL_BASE_DIR="$ac_milter_tmp" AC_MSG_RESULT([$SENDMAIL_BASE_DIR]) else AC_MSG_RESULT([no]) AC_MSG_CHECKING([for sendmail base from /etc/mail/sendmail.cf]) # # The previous method to find SENDMAIL_BASE_DIR failed, so we will # try this method. # # 1) Check for a line in /etc/mail/sendmail.cf of the form: # ##### in /some/path/sendmail-8.x.x/more/path # This is the directory that the sendmail.cf file was built in. # 2) Take the first occurrence if there are more than one. # 3) Remove the leading "##### in ". # 4) Remove everything after the sendmail-8.x.x path component. # dnl # Note that the following expression only should not use double dnl # square brackets because for some reason, possibly having to dnl # do with the pound sign, m4 doesn't convert them to single brackets. dnl # ac_milter_tmp=`grep "^##### in /" /etc/mail/sendmail.cf 2> /dev/null | grep "/sendmail-8.1" | sed '1q' | sed 's,^##### in ,,' | sed 's,\(/sendmail-8\.[0-9.]*\).*,\1,'` # Convert found version sections to three digit numbers by adding zeros. ac_milter_found_version=`echo "$ac_milter_tmp" | sed 's,.*/sendmail-,,;s,\([[0-9]]*\),x\1x,g;s,x\([[0-9]]\)x,x0\1x,g;s,x\([[0-9]][[0-9]]\)x,x0\1x,g;s,x,,g'` # ac_milter_version_ok will equal yes if the version is ok, otherwise no. ac_milter_version_ok=`echo "x$ac_milter_minimum_version x$ac_milter_found_version" | sort | sed '1q' | sed "s,x${ac_milter_minimum_version},yes,;s,x${ac_milter_found_version},no," ` if test -r "${ac_milter_tmp}/include/libmilter/mfapi.h" && \ test "$ac_milter_version_ok" = "yes" ; then # The file mfapi.h exists so we will use this as SENDMAIL_BASE_DIR. SENDMAIL_BASE_DIR="$ac_milter_tmp" AC_MSG_RESULT([$SENDMAIL_BASE_DIR]) else AC_MSG_RESULT([no]) fi fi fi ############################################################################# # # Determine the sendmail obj.* directory and set SENDMAIL_OBJ_DIR. # We can only do this if we found SENDMAIL_BASE_DIR. # if test "x$SENDMAIL_BASE_DIR" != "x" ; then if test "x$with_sendmail_obj" != "x" ; then # set SENDMAIL_OBJ_DIR to the one specified by--with-sendmail-obj SENDMAIL_OBJ_DIR="$with_sendmail_obj" else AC_MSG_CHECKING([for sendmail obj.* subdirectory using Build -M]) # # --with-sendmail-obj is not used, so we will try to determine it # # Try to run sendmail's Build program with the -M option which will # print out the name of the obj. directory for the tool in the # directory where it is run from. # ac_milter_tmp=`(cd ${SENDMAIL_BASE_DIR}/libmilter 1> /dev/null ; ./Build -M ) 2> /dev/null` if test -f "${ac_milter_tmp}/libmilter.a" ; then # libmilter.a exists so this is the one we will choose # Remove beginning and end of path from obj.* directory. SENDMAIL_OBJ_DIR=`echo "$ac_milter_tmp" | sed 's,/libmilter$,,;s,.*/,,'` AC_MSG_RESULT([${SENDMAIL_BASE_DIR}/${SENDMAIL_OBJ_DIR}]) else AC_MSG_RESULT([no]) AC_MSG_CHECKING([for sendmail obj.* subdirectory using ls]) # # List all directories of the form "obj." in the sendmail base # directory, and choose the one with the latest modification date. # ac_milter_tmp=`ls -dt ${SENDMAIL_BASE_DIR}/obj.*/libmilter 2> /dev/null | sed '1q'` if test -f "${ac_milter_tmp}/libmilter.a" ; then # libmilter.a exists so this is the one we will choose # Remove beginning and end of path from obj.* directory. SENDMAIL_OBJ_DIR=`echo "$ac_milter_tmp" | sed 's,/libmilter$,,;s,.*/,,'` AC_MSG_RESULT([${SENDMAIL_BASE_DIR}/${SENDMAIL_OBJ_DIR}]) else AC_MSG_RESULT([no]) fi fi fi fi ############################################################################# # # If we have both SENDMAIL_BASE_DIR and SENDMAIL_OBJ_DIR we will check # for the necessary files. # if test "x$SENDMAIL_BASE_DIR" != "x" && \ test "x$SENDMAIL_OBJ_DIR" != "x" ; then # Save and modify CPPFLAGS. ac_milter_save_CPPFLAGS="$CPPFLAGS" MILTER_CPPFLAGS="-I$SENDMAIL_BASE_DIR/include" CPPFLAGS="$CPPFLAGS $MILTER_CPPFLAGS" # Save and modify LDFLAGS. ac_milter_save_LDFLAGS="$LDFLAGS" MILTER_LDFLAGS="-L${SENDMAIL_BASE_DIR}/${SENDMAIL_OBJ_DIR}/libmilter" LDFLAGS="$MILTER_LDFLAGS $LDFLAGS" AC_CHECK_HEADER([libmilter/mfapi.h],[ AC_CHECK_LIB([milter],[smfi_main],[ # both tests succeeded so add -lmilter to the libraries to link MILTER_LIBS="-lmilter" # indicate success ax_path_milter_ok=yes ]) ]) # Restore the modified environment CPPFLAGS="$ac_milter_save_CPPFLAGS" LDFLAGS="$ac_milter_save_LDFLAGS" fi fi # If failure, clear MILTER_LIBS, MILTER_LDFLAGS and MILTER_CPPFLAGS. if test "$ax_path_milter_ok" = "no" ; then MILTER_CPPFLAGS="" MILTER_LIBS="" MILTER_LDFLAGS="" fi # export these to the make environment AC_SUBST([MILTER_LIBS]) AC_SUBST([MILTER_CPPFLAGS]) AC_SUBST([MILTER_LDFLAGS]) AC_SUBST([SENDMAIL_BASE_DIR]) AC_SUBST([SENDMAIL_OBJ_DIR]) # Indicate status of checking for libmilter stuff. AC_MSG_CHECKING([if files required by libmilter are present]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND. if test "$ax_path_milter_ok" = "yes" ; then AC_MSG_RESULT([yes]) $2 else AC_MSG_RESULT([no]) $3 fi ])dnl amavisd-milter-1.7.1/aclocal/dirfd.m4000066400000000000000000000013711372525560500174020ustar00rootroot00000000000000# Find out how to get the file descriptor associated with an open DIR*. AC_DEFUN([AC_CHECK_DIRFD], [ AH_TEMPLATE([HAVE_DIRFD_AS_MACRO], [Define if dirfd() is a macro.]) AC_REQUIRE([AC_PROG_CPP]) AC_REQUIRE([AC_PROG_EGREP]) AC_HEADER_DIRENT AC_CHECK_MEMBERS([DIR.d_fd, DIR.dd_fd, DIR.__dd_fd], [], [], [#include ]) AC_CHECK_FUNCS([dirfd]) AS_IF([test "$ac_cv_func_dirfd" = no], [AC_CACHE_CHECK([whether dirfd is a macro], [ac_cv_func_dirfd_macro], [AC_EGREP_CPP([func_dirfd_macro], [ #include #if defined(dirfd) func_dirfd_macro #endif],[ac_cv_func_dirfd_macro=yes],[ac_cv_func_dirfd_macro=no] ) ]) ]) if test "$ac_cv_func_dirfd_macro" = yes; then AC_DEFINE([HAVE_DIRFD_AS_MACRO], [1]) fi ]) amavisd-milter-1.7.1/aclocal/eilseq.m4000066400000000000000000000034751372525560500176030ustar00rootroot00000000000000# http://cvs.sourceforge.net/viewcvs.py/libiconv/m4/eilseq.m4 #serial 1 AC_PREREQ(2.50) # The EILSEQ errno value ought to be defined in , according to # ISO C 99 and POSIX. But some systems (like SunOS 4) don't define it, # and some systems (like BSD/OS) define it in not . # Define EILSEQ as a C macro and as a substituted macro in such a way that # 1. on all systems, after inclusion of , EILSEQ is usable, # 2. on systems where EILSEQ is defined elsewhere, we use the same numeric # value. AC_DEFUN([AC_EILSEQ], [ AC_REQUIRE([AC_PROG_CC])dnl dnl Check for any extra headers that could define EILSEQ. AC_CHECK_HEADERS(wchar.h) AC_CACHE_CHECK([for EILSEQ], ac_cv_decl_EILSEQ, [ AC_EGREP_CPP(yes,[ #include #ifdef EILSEQ yes #endif ], have_eilseq=1) if test -n "$have_eilseq"; then dnl EILSEQ exists in . Don't need to define EILSEQ ourselves. ac_cv_decl_EILSEQ=yes else AC_EGREP_CPP(yes,[ #include #if HAVE_WCHAR_H #include #endif #ifdef EILSEQ yes #endif ], have_eilseq=1) if test -n "$have_eilseq"; then dnl EILSEQ exists in some other system header. dnl Define it to the same value. _AC_COMPUTE_INT([EILSEQ], ac_cv_decl_EILSEQ, [ #include #if HAVE_WCHAR_H #include #endif /* The following two lines are a workaround against an autoconf-2.52 bug. */ #include #include ]) else dnl EILSEQ isn't defined by the system. Define EILSEQ ourselves. ac_cv_decl_EILSEQ=EINVAL fi fi ]) if test "$ac_cv_decl_EILSEQ" != yes; then AC_DEFINE_UNQUOTED([EILSEQ], [$ac_cv_decl_EILSEQ], [Define as good substitute value for EILSEQ.]) EILSEQ="$ac_cv_decl_EILSEQ" AC_SUBST(EILSEQ) fi ]) amavisd-milter-1.7.1/aclocal/eoverflow.m4000066400000000000000000000042541372525560500203250ustar00rootroot00000000000000# http://savannah.gnu.org/cgi-bin/viewcvs/gnulib/gnulib/m4/eoverflow.m4 # eoverflow.m4 serial 2 dnl Copyright (C) 2004, 2006 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible. # The EOVERFLOW errno value ought to be defined in , according to # POSIX. But some systems (like AIX 3) don't define it, and some systems # (like OSF/1) define it when _XOPEN_SOURCE_EXTENDED is defined. # Define EOVERFLOW as a C macro and as a substituted macro in such a way that # 1. on all systems, after inclusion of , EOVERFLOW is usable, # 2. on systems where EOVERFLOW is defined elsewhere, we use the same numeric # value. AC_DEFUN([gl_EOVERFLOW], [ AC_REQUIRE([AC_PROG_CC])dnl AC_CACHE_CHECK([for EOVERFLOW], ac_cv_decl_EOVERFLOW, [ AC_EGREP_CPP(yes,[ #include #ifdef EOVERFLOW yes #endif ], have_eoverflow=1) if test -n "$have_eoverflow"; then dnl EOVERFLOW exists in . Don't need to define EOVERFLOW ourselves. ac_cv_decl_EOVERFLOW=yes else AC_EGREP_CPP(yes,[ #define _XOPEN_SOURCE_EXTENDED 1 #include #ifdef EOVERFLOW yes #endif ], have_eoverflow=1) if test -n "$have_eoverflow"; then dnl EOVERFLOW exists but is hidden. dnl Define it to the same value. AC_COMPUTE_INT([EOVERFLOW], ac_cv_decl_EOVERFLOW, [ #define _XOPEN_SOURCE_EXTENDED 1 #include /* The following two lines are a workaround against an autoconf-2.52 bug. */ #include #include ]) else dnl EOVERFLOW isn't defined by the system. Define EOVERFLOW ourselves, but dnl don't define it as EINVAL, because snprintf() callers want to dnl distinguish EINVAL and EOVERFLOW. ac_cv_decl_EOVERFLOW=E2BIG fi fi ]) if test "$ac_cv_decl_EOVERFLOW" != yes; then AC_DEFINE_UNQUOTED([EOVERFLOW], [$ac_cv_decl_EOVERFLOW], [Define as good substitute value for EOVERFLOW.]) EOVERFLOW="$ac_cv_decl_EOVERFLOW" AC_SUBST(EOVERFLOW) fi ]) amavisd-milter-1.7.1/amavisd-milter/000077500000000000000000000000001372525560500173665ustar00rootroot00000000000000amavisd-milter-1.7.1/amavisd-milter/.gitignore000066400000000000000000000000731372525560500213560ustar00rootroot00000000000000.deps Makefile Makefile.in amavisd-milter amavisd-milter.8 amavisd-milter-1.7.1/amavisd-milter/Makefile.am000066400000000000000000000012731372525560500214250ustar00rootroot00000000000000# Distribution files EXTRA_DIST= \ amavisd-milter.8 \ Makefile.am \ Makefile.in # Header files noinst_HEADERS= \ amavisd-milter.h # Manual pages man_MANS= \ amavisd-milter.8 # Binaries sbin_PROGRAMS= \ amavisd-milter # amavisd-milter compiling parameters amavisd_milter_SOURCES= \ amavisd.c \ log.c \ main.c \ mlfi.c amavisd_milter_LDADD= \ ../compat/libcompat.a amavisd_milter_CPPFLAGS= \ -I../compat amavisd-milter.8: ../AMAVISD-MILTER.md cat ../AMAVISD-MILTER.md | \ sed "s/^# amavisd-milter(8)/% amavisd-milter(8) Version ${VERSION} | System Manager's Manual/" | \ sed "s/^#//" | \ pandoc -s -t man > $@ maintainer-clean-local: -rm -f Makefile.in -rm -f amavisd-milter.8 amavisd-milter-1.7.1/amavisd-milter/amavisd-milter.h000066400000000000000000000141201372525560500224530ustar00rootroot00000000000000/* * Copyright (c) 2005, Petr Rehor . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _AMAVISD_MILTER_H #define _AMAVISD_MILTER_H #include "compat.h" /* AM.PDP protocol version */ #define AMPDP_VERSION 2 /* Maximum message buffers length */ #define MAXLOGBUF 1024 /* syslog message buffer */ #define MAXAMABUF 65536 /* amavisd communication buffer */ #define AMABUFCHUNK 2048 /* amavisd buffer reallocation step */ /* Timeouts */ #define SMFI_PROGRESS_TRIGGER 60 /* smfi_progress trigger */ struct mlfiCtx; /* Address list */ struct mlfiAddress { struct mlfiAddress *q_next; /* next recipient */ char q_paddr[1]; /* recipient */ }; /* Milter private data structure */ struct mlfiCtx { char *mlfi_daemon_name; /* sendmail daemon name */ char *mlfi_hostname; /* sendmail hostname */ char *mlfi_client_addr; /* remote host address */ char *mlfi_client_host; /* remote host name or address */ char *mlfi_client_name; /* remote host name */ char *mlfi_helo; /* remote host helo */ char *mlfi_protocol; /* communication protocol */ char *mlfi_qid; /* queue id */ char *mlfi_prev_qid; /* previous queue id */ char *mlfi_from; /* mail sender */ struct mlfiAddress *mlfi_rcpt; /* mail recipients */ char mlfi_wrkdir[MAXPATHLEN];/* working directory */ char mlfi_fname[MAXPATHLEN]; /* mail file name */ FILE *mlfi_fp; /* mail file handler */ int mlfi_max_sem_locked; /* connections semaphore locked */ char *mlfi_amabuf; /* amavisd communication buffer */ size_t mlfi_amabuf_length; /* amavisd buffer length */ int mlfi_amasd; /* amavisd socket descriptor */ char *mlfi_policy_bank; /* policy bank names */ int mlfi_cr_flag; /* CR at the end of the body chunk */ }; /* Get private data from libmilter */ #define MLFICTX(ctx) ((struct mlfiCtx *)smfi_getpriv(ctx)) /* Milter description */ extern struct smfiDesc smfilter; /* Milter functions */ extern sfsistat mlfi_connect(SMFICTX *, char *, _SOCK_ADDR *); extern sfsistat mlfi_helo(SMFICTX *, char *); extern sfsistat mlfi_envfrom(SMFICTX *, char **); extern sfsistat mlfi_envrcpt(SMFICTX *, char **); extern sfsistat mlfi_header(SMFICTX *, char *, char *); extern sfsistat mlfi_eoh(SMFICTX *); extern sfsistat mlfi_body(SMFICTX *, unsigned char *, size_t); extern sfsistat mlfi_eom(SMFICTX *); extern sfsistat mlfi_close(SMFICTX *); extern sfsistat mlfi_abort(SMFICTX *); /* Global variables */ extern int policybank_from_daemon_name; /* Select Policybank from Miltermacro daemon_name */ extern int daemonize; /* run as daemon */ extern int daemonized; /* is daemon */ extern int debug_level; /* max debug level */ extern int max_conns; /* max amavisd connections */ extern int max_wait; /* max wait for connection */ extern sem_t *max_sem; /* amavisd connections semaphore */ extern const char *pid_file; /* pid file name */ extern char *mlfi_socket; /* sendmail milter socket */ #ifdef HAVE_SMFI_SETBACKLOG extern int mlfi_socket_backlog; /* milter socket backlog */ #endif extern long mlfi_timeout; /* connection timeout */ extern const char *amavisd_socket; /* amavisd socket */ extern long amavisd_timeout; /* connection timeout */ extern int ignore_amavisd_error; /* pass through when amavisd failed */ extern const char *working_dir; /* working ditectory name */ extern const char *delivery_care_of; /* delivery mechanism */ /* Amavisd communication */ extern int amavisd_connect(struct mlfiCtx *, struct sockaddr_un *, time_t timeout); extern int amavisd_request(struct mlfiCtx *, const char *, const char *); extern int amavisd_response(struct mlfiCtx *); extern void amavisd_close(struct mlfiCtx *); /* errno value if amavisd_connect() timed out. */ #ifdef HAVE_SEM_TIMEDWAIT # define AMAVISD_CONNECT_TIMEDOUT_ERRNO ETIMEDOUT #else # define AMAVISD_CONNECT_TIMEDOUT_ERRNO EAGAIN #endif /* Log message */ extern void logmsg(int, const char *, ...); extern void logqidmsg(struct mlfiCtx *, int, const char *, ...); /* Macros */ #ifndef MAX #define MAX(a,b) (((a)>(b))?(a):(b)) #endif #ifndef MIN #define MIN(a,b) (((a)<(b))?(a):(b)) #endif #endif /* _AMAVISD_MILTER_H */ amavisd-milter-1.7.1/amavisd-milter/amavisd.c000066400000000000000000000206451372525560500211650ustar00rootroot00000000000000/* * Copyright (c) 2005, Petr Rehor . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "amavisd-milter.h" #include /* ** AMAVISD_GROW_AMABUF - Reallocate amavisd communication buffer */ static void * amavisd_grow_amabuf(struct mlfiCtx *mlfi, char *b) { char *amabuf; size_t buflen, bufpos; /* Calculate buffer pointer position */ if (b == NULL) { bufpos = 0; } else { bufpos = b - mlfi->mlfi_amabuf; } /* Calculate new buffer size */ buflen = mlfi->mlfi_amabuf_length + AMABUFCHUNK; if (mlfi->mlfi_amabuf_length < MAXAMABUF && buflen >= MAXAMABUF) { logqidmsg(mlfi, LOG_WARNING, "maximum size of amavisd communication buffer was reached"); buflen = MAXAMABUF; } else if (buflen > MAXAMABUF) { logqidmsg(mlfi, LOG_ERR, "amavisd communication buffer is too big (%lu)", (unsigned long)buflen); errno = EOVERFLOW; return NULL; } /* Reallocate buffer */ if ((amabuf = realloc(mlfi->mlfi_amabuf, buflen)) == NULL) { logqidmsg(mlfi, LOG_ERR, "could not reallocate amavisd communication buffer (%lu)", (unsigned long)buflen); return NULL; } mlfi->mlfi_amabuf = amabuf; mlfi->mlfi_amabuf_length = buflen; logqidmsg(mlfi, LOG_DEBUG, "amavisd communication buffer was increased to %lu", (unsigned long)buflen); return amabuf + bufpos; } /* ** AMAVISD_CONNECT - Connect to amavisd socket */ int amavisd_connect(struct mlfiCtx *mlfi, struct sockaddr_un *sock, time_t timeout) { int i; #ifdef HAVE_SEM_TIMEDWAIT struct timespec max_timeout; #endif /* Lock amavisd connection */ if (max_sem != NULL && mlfi->mlfi_max_sem_locked == 0) { #ifdef HAVE_SEM_TIMEDWAIT max_timeout.tv_sec = timeout; max_timeout.tv_nsec = 0; while ((i = sem_timedwait(max_sem, &max_timeout)) != 0 && errno == EINTR) { continue; } #else while ((i = sem_trywait(max_sem)) != 0 && errno == EAGAIN && time(NULL) < timeout) { sleep(1); } #endif if (i == -1) { if (errno != AMAVISD_CONNECT_TIMEDOUT_ERRNO) { logqidmsg(mlfi, LOG_ERR, "could not lock amavisd connections semaphore: %s", strerror(errno)); } return -1; } mlfi->mlfi_max_sem_locked = 1; sem_getvalue(max_sem, &i); logqidmsg(mlfi, LOG_DEBUG, "grab amavisd connection %d", i); } /* Initialize domain socket */ memset(sock, '\0', sizeof(*sock)); sock->sun_family = AF_UNIX; (void) strlcpy(sock->sun_path, amavisd_socket, sizeof(sock->sun_path)); if ((mlfi->mlfi_amasd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) { logqidmsg(mlfi, LOG_ERR, "could not create amavisd socket %s: %s", amavisd_socket, strerror(errno)); return -1; } /* Connect to amavisd */ if (connect(mlfi->mlfi_amasd, (struct sockaddr *)sock, sizeof(*sock)) == -1) { logqidmsg(mlfi, LOG_ERR, "could not connect to amavisd socket %s: %s", amavisd_socket, strerror(errno)); return -1; } /* Return socket */ logqidmsg(mlfi, LOG_DEBUG, "open amavisd communication socket %s", amavisd_socket); return mlfi->mlfi_amasd; } /* ** AMAVISD_REQUEST - Write request line to amavisd */ int amavisd_request(struct mlfiCtx *mlfi, const char *name, const char *value) { const char *p; char *b = mlfi->mlfi_amabuf; /* Encode request */ if (name != NULL) { p = name; while (*p != '\0') { if (b >= mlfi->mlfi_amabuf + mlfi->mlfi_amabuf_length - 5 && (b = amavisd_grow_amabuf(mlfi, b)) == NULL) { return -1; } if (isalnum(*p) || *p == '-' || *p == '_') { *b++ = *p++; } else { (void) snprintf(b, 4, "%%%02x", *p++); b += 3; } } if (value != NULL) { *b++ = '='; } } if (value != NULL) { p = value; while (*p != '\0') { if (b >= mlfi->mlfi_amabuf + mlfi->mlfi_amabuf_length - 4 && (b = amavisd_grow_amabuf(mlfi, b)) == NULL) { return -1; } if (isalnum(*p) || *p == '-' || *p == '_') { *b++ = *p++; } else { (void) snprintf(b, 4, "%%%02x", *p++); b += 3; } } } *b++ = '\n'; /* Write request to amavisd socket */ return write_sock(mlfi->mlfi_amasd, mlfi->mlfi_amabuf, b - mlfi->mlfi_amabuf, amavisd_timeout); } /* ** AMAVISD_RESPONSE - Read response line from amavisd */ int amavisd_response(struct mlfiCtx *mlfi) { int decode = 0; char *b = mlfi->mlfi_amabuf; char *b2; /* Read response line */ while (read_sock(mlfi->mlfi_amasd, b, 1, amavisd_timeout) > 0) { if (b >= mlfi->mlfi_amabuf + mlfi->mlfi_amabuf_length - 2) { if ((b2 = amavisd_grow_amabuf(mlfi, b)) == NULL) { *(b + 1) = '\0'; return -1; } else { b = b2; } } if (*b == '\n') { *b = '\0'; return 0; } else if (*b == '%') { decode = 1; } else if (decode == 1) { if (isxdigit(*b)) { decode = 2; b++; } else { *(b + 1) = '\0'; errno = EILSEQ; return -1; } } else if (decode == 2) { if (isxdigit(*b)) { *(b + 1) = '\0'; *(b - 1) = (u_char) strtol(b - 1, NULL, 16); decode = 0; } else { *(b + 1) = '\0'; errno = EILSEQ; return -1; } } else if (*b == '\r') { /* Do nothing */ } else { b++; } } /* read_sock failed */ *b = '\0'; return -1; } /* ** AMAVISD_CLOSE - Close amavisd socket */ void amavisd_close(struct mlfiCtx *mlfi) { /* Close amavisd connection */ if (mlfi->mlfi_amasd != -1) { if (close(mlfi->mlfi_amasd) == -1) { logqidmsg(mlfi, LOG_ERR, "could not close amavisd socket %s: %s", mlfi->mlfi_fname, strerror(errno)); } mlfi->mlfi_amasd = -1; logqidmsg(mlfi, LOG_DEBUG, "close amavisd communication socket"); } /* Unlock amavisd connection */ if (mlfi->mlfi_max_sem_locked != 0) { if (sem_post(max_sem) == -1) { logqidmsg(mlfi, LOG_ERR, "%s: could not unlock amavisd connections semaphore: %s", strerror(errno)); } mlfi->mlfi_max_sem_locked = 0; logqidmsg(mlfi, LOG_DEBUG, "got back amavisd connection"); } } amavisd-milter-1.7.1/amavisd-milter/log.c000066400000000000000000000055711372525560500203230ustar00rootroot00000000000000/* * Copyright (c) 2005, Petr Rehor . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "amavisd-milter.h" #include /* ** LOGMSG - Print log message */ void logmsg(int priority, const char *fmt, ...) { char buf[MAXLOGBUF]; va_list ap; if (priority <= debug_level || priority <= LOG_WARNING) { /* Format message */ va_start(ap, fmt); (void) vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); /* Write message to syslog */ syslog(priority, "%s", buf); /* Print message to terminal */ if (!daemonized) { (void) fprintf(stdout, "%s\n", buf); } } } /* ** LOGQIDMSG - Log message with mail queue id */ void logqidmsg(struct mlfiCtx *mlfi, int priority, const char *fmt, ...) { char buf[MAXLOGBUF]; const char *p; va_list ap; /* Format message */ va_start(ap, fmt); (void) vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); /* Print log message */ if (mlfi != NULL) { if (mlfi->mlfi_qid != NULL) { p = mlfi->mlfi_qid; } else if (mlfi->mlfi_prev_qid != NULL) { p = mlfi->mlfi_prev_qid; } else if (mlfi->mlfi_client_host != NULL) { p = mlfi->mlfi_client_host; } else { p = "UNKNOWN"; } } else { p = "NOQUEUE"; } logmsg(priority, "%s: %s", p, buf); } amavisd-milter-1.7.1/amavisd-milter/main.c000066400000000000000000000404661372525560500204700ustar00rootroot00000000000000/* * Copyright (c) 2005, Petr Rehor . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "amavisd-milter.h" #include #include #include /* ** GLOBAL VARIABLES */ int daemonize = 1; int daemonized = 0; int debug_level = LOG_WARNING; int max_conns = 0; int max_wait = 5 * 60; sem_t max_sem_t; sem_t *max_sem = NULL; const char *pid_file = LOCAL_STATE_DIR "/" PACKAGE ".pid"; char *mlfi_socket = LOCAL_STATE_DIR "/" PACKAGE ".sock"; #ifdef HAVE_SMFI_SETBACKLOG int mlfi_socket_backlog = 0; #endif long mlfi_timeout = 600; const char *amavisd_socket = LOCAL_STATE_DIR "/amavisd.sock"; long amavisd_timeout = 600; int ignore_amavisd_error = 0; const char *working_dir = WORKING_DIR; const char *delivery_care_of = "client"; int policybank_from_daemon_name = 0; /* ** USAGE - Print usage info */ static void usage(const char *progname) { (void) fprintf(stdout, "\nUsage: %s [OPTIONS]\n", progname); (void) fprintf(stdout, "Options are:\n"); (void) fprintf(stdout, " -B Use daemon_name policy bank\n"); (void) fprintf(stdout, " -d debug-level Set debug level\n"); (void) fprintf(stdout, " -D delivery Delivery care of server or client\n"); (void) fprintf(stdout, " -f Run in the foreground\n"); (void) fprintf(stdout, " -h Print this page\n"); (void) fprintf(stdout, " -m max-conns Maximum amavisd connections \n"); (void) fprintf(stdout, " -M max-wait Maximum wait for connection in seconds\n"); (void) fprintf(stdout, " -p pidfile Use this pid file\n"); (void) fprintf(stdout, " -P When amavisd fails mail will be passed\n through unchecked\n"); #ifdef HAVE_SMFI_SETBACKLOG (void) fprintf(stdout, " -q backlog Milter communication socket backlog\n"); #endif (void) fprintf(stdout, " -s socket Milter communication socket\n"); (void) fprintf(stdout, " -S socket Amavisd communication socket\n"); (void) fprintf(stdout, " -t timeout Milter connection timeout in seconds\n"); (void) fprintf(stdout, " -T timeout Amavisd connection timeout in seconds\n"); (void) fprintf(stdout, " -v Report the version and exit\n"); (void) fprintf(stdout, " -w directory Set the working directory\n\n"); } /* ** USAGEERR - Print error message, program usage and then exit */ static void usageerr(const char *progname, const char *fmt, ...) { char buf[MAXLOGBUF]; va_list ap; /* Format err message */ va_start(ap, fmt); (void) vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); /* Print error message, program usage and then exit */ (void) fprintf(stderr, "%s: %s\n", progname , buf); usage(progname); exit(EX_USAGE); } /* ** VERSIONINFO - Print program version info */ static void versioninfo(const char *progname) { (void) fprintf(stdout, "%s %s\n", progname, VERSION); } /* ** MAIN - Main program loop */ int main(int argc, char *argv[]) { static const char *args = "Bd:D:fhm:M:p:Pq:s:S:t:T:vw:"; int c, rstat; char *p; const char *progname, *socket_name; FILE *fp; struct stat st; mode_t save_umask; struct sockaddr_un unix_addr; /* Program name */ p = strrchr(argv[0], '/'); if (p == NULL) { progname = argv[0]; } else { progname = p + 1; } /* Open syslog */ openlog(progname, LOG_PID, LOG_MAIL); /* Process command line options */ while ((c = getopt(argc, argv, args)) != EOF) { switch (c) { case 'B': /* use daemon_name policy bank */ policybank_from_daemon_name = 1; break; case 'd': /* debug level */ if (optarg == NULL || *optarg == '\0') { usageerr(progname, "option requires an argument -- %c", (char)c); } debug_level = (int) strtol(optarg, &p, 10); if (p != NULL && *p != '\0') { usageerr(progname, "debug level is not valid number: %s", optarg); } if (debug_level < 0) { usageerr(progname, "negative debug level: %d", debug_level); } debug_level += LOG_WARNING; break; case 'D': /* delivery mechanism */ if (optarg == NULL || *optarg == '\0') { usageerr(progname, "option requires an argument -- %c", (char)c); } if (strcmp(optarg, "client") != 0 && strcmp(optarg, "server") != 0) { usageerr(progname, "unknown delivery mechanism '%s'", optarg); } delivery_care_of = optarg; break; case 'f': /* run in foreground */ daemonize = 0; break; case '?': /* options parsing error */ (void) fprintf(stderr, "\n"); case 'h': /* help */ usage(progname); exit(EX_OK); break; case 'm': /* maximum amavisd connections */ max_conns = (int) strtol(optarg, &p, 10); if (p != NULL && *p != '\0') { usageerr(progname, "maximum amavisd connections is not valid number: %s", optarg); } if (max_conns < 0) { usageerr(progname, "negative maximum amavisd connections: %d", max_conns); } break; case 'M': /* maximum wait for connection */ max_wait = (int) strtol(optarg, &p, 10); if (p != NULL && *p != '\0') { usageerr(progname, "maximum wait for connection is not valid number: %s", optarg); } if (max_wait < 0) { usageerr(progname, "negative maximum wait for connection: %d", max_wait); } break; case 'p': /* pid file name */ if (optarg == NULL || *optarg == '\0') { usageerr(progname, "option requires an argument -- %c", (char)c); } pid_file = optarg; break; case 'P': /* when amavisd fails mail will be passed */ ignore_amavisd_error = 1; /* through unchecked */ break; #ifdef HAVE_SMFI_SETBACKLOG case 'q': /* milter communication socket backlog */ mlfi_socket_backlog = (int) strtol(optarg, &p, 10); if (p != NULL && *p != '\0') { usageerr(progname, "milter communication socket backlog is not valid number: " "%s", optarg); } if (mlfi_socket_backlog < 0) { usageerr(progname, "negative milter communication socket backlog: %d", mlfi_socket_backlog); } break; #endif case 's': /* milter communication socket */ if (optarg == NULL || *optarg == '\0') { usageerr(progname, "option requires an argument -- %c", (char)c); } if (strlen(optarg) >= sizeof(unix_addr.sun_path) - 1) { usageerr(progname, "milter communication socket name too long: %s", optarg); } mlfi_socket = optarg; break; case 't': /* milter connection timeout */ if (optarg == NULL || *optarg == '\0') { usageerr(progname, "option requires an argument -- %c", (char)c); } mlfi_timeout = (int) strtol(optarg, &p, 10); if (p != NULL && *p != '\0') { usageerr(progname, "milter connection timeout is not valid number: %s", optarg); } if (mlfi_timeout < 0) { usageerr(progname, "negative milter connection timeout: %ld", mlfi_timeout); } break; case 'v': /* version info */ versioninfo(progname); exit(EX_OK); break; case 'w': /* working directory */ if (optarg == NULL || *optarg == '\0') { usageerr(progname, "option requires an argument -- %c", (char)c); } working_dir = optarg; break; case 'S': /* amavisd communication socket */ if (optarg == NULL || *optarg == '\0') { usageerr(progname, "option requires an argument -- %c", (char)c); } if (strlen(optarg) >= sizeof(unix_addr.sun_path) - 1) { usageerr(progname, "amavisd communication socket name too long: %s", optarg); } amavisd_socket = optarg; break; case 'T': /* amavisd connection timeout */ if (optarg == NULL || *optarg == '\0') { usageerr(progname, "option requires an argument -- %c", (char)c); } amavisd_timeout = (int) strtol(optarg, &p, 10); if (p != NULL && *p != '\0') { usageerr(progname, "amavisd connection timeout is not valid number: %s", optarg); } if (amavisd_timeout < 0) { usageerr(progname, "negative amavisd connection timeout: %ld", amavisd_timeout); } break; default: /* unknown option */ usageerr(progname, "illegal option -- %c", (char)c); break; } } /* Create amavisd connections semaphore */ if (max_conns > 0) { if (sem_init(&max_sem_t, 0, max_conns) == -1) { logmsg(LOG_ERR, "could not initialize amavisd connections semaphore: %s", strerror(errno)); exit(EX_SOFTWARE); } else { max_sem = &max_sem_t; } } /* Check permissions on working directory */ /* TODO: traverse working directory path */ if (stat(working_dir, &st) != 0) { logmsg(LOG_ERR, "could not stat() to working directory %s: %s", working_dir, strerror(errno)); exit(EX_SOFTWARE); } if (!S_ISDIR(st.st_mode)) { logmsg(LOG_ERR, "%s is not directory", working_dir); exit(EX_SOFTWARE); } if ((st.st_mode & S_IRWXO) != 0) { logmsg(LOG_ERR, "working directory %s is world accessible", working_dir); exit(EX_SOFTWARE); } /* Configure milter */ socket_name = NULL; if (mlfi_socket[0] == '/') { socket_name = mlfi_socket; } if (! strncmp(mlfi_socket, "unix:", 5)) { socket_name = mlfi_socket + 5; } if (! strncmp(mlfi_socket, "local:", 6)) { socket_name = mlfi_socket + 6; } if (socket_name != NULL && unlink(socket_name) != 0 && errno != ENOENT) { logmsg(LOG_ERR, "could not unlink old milter socket %s: %s", socket_name, strerror(errno)); exit(EX_SOFTWARE); } if (debug_level > LOG_DEBUG && smfi_setdbg(debug_level - LOG_DEBUG) != MI_SUCCESS) { logmsg(LOG_ERR, "could not set milter debug level"); exit(EX_SOFTWARE); } if (mlfi_timeout > 0 && smfi_settimeout(mlfi_timeout) != MI_SUCCESS) { logmsg(LOG_ERR, "could not set milter timeout"); exit(EX_SOFTWARE); } if (smfi_setconn(mlfi_socket) != MI_SUCCESS) { logmsg(LOG_ERR, "could not set milter socket"); exit(EX_SOFTWARE); } if (smfi_register(smfilter) != MI_SUCCESS) { logmsg(LOG_ERR, "could not register milter"); exit(EX_SOFTWARE); } /* Unlink old pid file */ if (pid_file != NULL) { if (unlink(pid_file) != 0 && errno != ENOENT) { logmsg(LOG_WARNING, "could not unlink old pid file %s: %s", pid_file, strerror(errno)); } } /* Connect to milter socket */ #ifdef HAVE_SMFI_SETBACKLOG if (mlfi_socket_backlog > 0) { if (smfi_setbacklog(mlfi_socket_backlog) != MI_SUCCESS) { logmsg(LOG_WARNING, "could not set milter socket backlog to %d", mlfi_socket_backlog); } } #endif #ifdef HAVE_SMFI_OPENSOCKET if (smfi_opensocket(false) != MI_SUCCESS) { logmsg(LOG_ERR, "could not open milter socket %s", mlfi_socket); exit(EX_SOFTWARE); } #endif /* Run in the background */ if (daemonize) { if (daemon(1, 1) != -1) { daemonized = 1; } else { logmsg(LOG_ERR, "could not fork daemon process: %s", strerror(errno)); exit(EX_OSERR); } } /* Greetings message */ logmsg(LOG_WARNING, "starting %s %s on socket %s", progname, VERSION, mlfi_socket); /* Create pid file */ if (pid_file != NULL) { save_umask = umask(022); fp = fopen(pid_file, "w"); if (fp == NULL) { logmsg(LOG_WARNING, "could not create pid file %s: %s", pid_file, strerror(errno)); } else { (void) fprintf(fp, "%ld\n", (long) getpid()); if (ferror(fp)) { logmsg(LOG_WARNING, "could not write to pid file %s: %s", pid_file, strerror(errno)); clearerr(fp); (void) fclose(fp); } else if (fclose(fp) != 0) { logmsg(LOG_WARNING, "could not close pid file %s: %s", pid_file, strerror(errno)); } } umask(save_umask); } /* Run milter */ if ((rstat = smfi_main()) != MI_SUCCESS) { logmsg(LOG_ERR, "%s failed", progname); } else { logmsg(LOG_WARNING, "stopping %s %s on socket %s", progname, VERSION, mlfi_socket); } /* Unlink pid file */ if (pid_file != NULL) { if (unlink(pid_file) != 0) { logmsg(LOG_WARNING, "could not unlink pid file %s: %s", pid_file, strerror(errno)); } } /* Destroy amavisd connections semaphore */ if (max_sem != NULL && sem_destroy(max_sem) == -1) { logmsg(errno == EBUSY ? LOG_ERR : LOG_WARNING, "%s: could not destroy amavisd connections semaphore: %s", progname, strerror(errno)); } return rstat; } amavisd-milter-1.7.1/amavisd-milter/mlfi.c000066400000000000000000001560721372525560500204740ustar00rootroot00000000000000/* * Copyright (c) 2005, Petr Rehor . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "amavisd-milter.h" #include #include #include /* ** SMFILTER - Milter description */ struct smfiDesc smfilter = { PACKAGE, /* filter name */ SMFI_VERSION, /* version code -- do not change */ SMFIF_ADDHDRS | SMFIF_CHGHDRS | SMFIF_ADDRCPT | SMFIF_DELRCPT, /* filter actions */ mlfi_connect, /* connection info filter */ mlfi_helo, /* SMTP HELO command filter */ mlfi_envfrom, /* envelope sender filter */ mlfi_envrcpt, /* envelope recipient filter */ mlfi_header, /* header filter */ mlfi_eoh, /* end of header */ mlfi_body, /* body block filter */ mlfi_eom, /* end of message */ mlfi_abort, /* message aborted */ mlfi_close, /* connection cleanup */ NULL, /* any unrecognized or unimplemented */ /* command filter */ NULL, /* SMTP DATA command filter */ NULL /* negotiation callback */ }; /* ** Dates and months abbreviations */ static const char *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; /* ** SNPRINTFCAT - Append formatted string */ static size_t snprintfcat(size_t length, char *buf, size_t size, const char *fmt, ...) { va_list ap; va_start(ap, fmt); if ((length += vsnprintf(buf + length, size - length, fmt, ap)) >= size) { length = size -1; } va_end(ap); return length; } /* ** MLFI_CLEANUP_MESSAGE - Cleanup message context ** ** mlfi_cleanup_message() close message file if open, unlink working directory ** and release message context */ static void mlfi_cleanup_message(struct mlfiCtx *mlfi) { FTS *fts; FTSENT *ftsent; char *wrkdir[] = { NULL, NULL }; struct mlfiAddress *rcpt; logqidmsg(mlfi, LOG_DEBUG, "CLEANUP MESSAGE CONTEXT"); /* Check milter private data */ if (mlfi == NULL) { logqidmsg(mlfi, LOG_DEBUG, "mlfi_cleanup_message: context is not set"); return; } /* Close amavisd connection */ amavisd_close(mlfi); /* Close the message file */ if (mlfi->mlfi_fp != NULL) { if (fclose(mlfi->mlfi_fp) != 0 && errno != EBADF) { logqidmsg(mlfi, LOG_WARNING, "could not close message file %s: %s", mlfi->mlfi_fname, strerror(errno)); } else { logqidmsg(mlfi, LOG_DEBUG, "close message file %s", mlfi->mlfi_fname); } mlfi->mlfi_fp = NULL; } /* Remove working directory */ if (mlfi->mlfi_wrkdir[0] != '\0') { wrkdir[0] = mlfi->mlfi_wrkdir; fts = fts_open(wrkdir, FTS_PHYSICAL | FTS_NOCHDIR, NULL); if (fts == NULL) { logqidmsg(mlfi, LOG_WARNING, "could not open file hierarchy %s: %s", mlfi->mlfi_wrkdir, strerror(errno)); } else { while ((ftsent = fts_read(fts)) != NULL) { switch (ftsent->fts_info) { case FTS_ERR: /* * This is an error return, and the fts_errno * field will be set to indicate what caused the * error. */ logqidmsg(mlfi, LOG_WARNING, "could not traverse file hierarchy %s: %s", ftsent->fts_path, strerror(ftsent->fts_errno)); break; case FTS_DNR: /* * Assume that since fts_read() couldn't read the * directory, it can't be removed. */ if (ftsent->fts_errno != ENOENT) { logqidmsg(mlfi, LOG_WARNING, "could not remove directory %s: %s", ftsent->fts_path, strerror(ftsent->fts_errno)); } break; case FTS_NS: /* * Assume that since fts_read() couldn't stat the * file, it can't be unlinked. */ logqidmsg(mlfi, LOG_WARNING, "could not unlink file %s: %s", ftsent->fts_path, strerror(ftsent->fts_errno)); break; case FTS_D: /* * Skip pre-order directory. */ break; case FTS_DP: /* * Remove post-order directory. */ if (rmdir(ftsent->fts_accpath) != 0 && errno != ENOENT) { logqidmsg(mlfi, LOG_WARNING, "could not remove directory %s: %s", ftsent->fts_path, strerror(ftsent->fts_errno)); } else { logqidmsg(mlfi, LOG_DEBUG, "remove directory %s", ftsent->fts_path); } break; default: /* * A regular file or symbolic link. */ if (unlink(ftsent->fts_accpath) != 0 && errno != ENOENT) { logqidmsg(mlfi, LOG_WARNING, "could not unlink file %s: %s", ftsent->fts_path, strerror(ftsent->fts_errno)); } else { logqidmsg(mlfi, LOG_DEBUG, "unlink file %s", ftsent->fts_path); } } } if (fts_close(fts) != 0) { logqidmsg(mlfi, LOG_WARNING, "could not close file hirerachy %s: %s", mlfi->mlfi_wrkdir, strerror(errno)); } } mlfi->mlfi_wrkdir[0] = '\0'; } /* Reset CRLF detection flag */ mlfi->mlfi_cr_flag = 0; /* Free memory */ free(mlfi->mlfi_prev_qid); mlfi->mlfi_prev_qid = mlfi->mlfi_qid; mlfi->mlfi_qid = NULL; free(mlfi->mlfi_from); mlfi->mlfi_from = NULL; free(mlfi->mlfi_policy_bank); mlfi->mlfi_policy_bank = NULL; while(mlfi->mlfi_rcpt != NULL) { rcpt = mlfi->mlfi_rcpt; mlfi->mlfi_rcpt = rcpt->q_next; free(rcpt); } } /* ** MLFI_CLEANUP - Cleanup connection context ** ** mlfi_cleanup() cleanup message context and relese connection context */ static void mlfi_cleanup(struct mlfiCtx *mlfi) { /* Check milter private data */ if (mlfi == NULL) { logqidmsg(mlfi, LOG_DEBUG, "CLEANUP CONNECTION CONTEXT"); logqidmsg(mlfi, LOG_DEBUG, "mlfi_cleanup: context is not set"); return; } /* Cleanup the message context */ mlfi_cleanup_message(mlfi); logqidmsg(mlfi, LOG_DEBUG, "CLEANUP CONNECTION CONTEXT"); /* Cleanup the connection context */ free(mlfi->mlfi_daemon_name); free(mlfi->mlfi_hostname); free(mlfi->mlfi_client_addr); free(mlfi->mlfi_client_host); free(mlfi->mlfi_client_name); free(mlfi->mlfi_helo); free(mlfi->mlfi_protocol); free(mlfi->mlfi_amabuf); free(mlfi->mlfi_prev_qid); /* Free context */ free(mlfi); } /* ** MLFI_SETREPLY_TEMPFAIL - Set SMFIS_TEMPFAIL reply */ static void mlfi_setreply_tempfail(SMFICTX *ctx) { char *rcode = "451"; char *xcode = "4.6.0"; char *reason = "Content scanner malfunction"; struct mlfiCtx *mlfi = MLFICTX(ctx); if (smfi_setreply(ctx, rcode, xcode, reason) != MI_SUCCESS) { logqidmsg(mlfi, LOG_WARNING, "could not set SMTP reply: %s %s %s", rcode, xcode, reason); } else { logqidmsg(mlfi, LOG_DEBUG, "set reply %s %s %s", rcode, xcode, reason); } } /* ** MLFI_CONNECT - Handle incomming connection ** ** mlfi_connect() is called once, at the start of each SMTP connection */ sfsistat mlfi_connect(SMFICTX *ctx, char *client_host, _SOCK_ADDR * hostaddr) { struct mlfiCtx *mlfi = NULL; const void *addr; const char *prefix; const char *client_name; const char *client_resolve; const char *daemon_name; const char *hostname; int len, plen; /* Be sure we have client_host */ if (client_host == NULL || *client_host == '\0') { client_host = "unknown"; } logmsg(LOG_DEBUG, "%s: CONNECT", client_host); /* Allocate memory for private data */ mlfi = malloc(sizeof(*mlfi)); if (mlfi == NULL) { logmsg(LOG_ERR, "%s: could not allocate private data", client_host); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* Initialize context */ (void) memset(mlfi, '\0', sizeof(*mlfi)); mlfi->mlfi_amasd = -1; /* Save client hostname (Reverse DNS or IP addresss in square bracket) */ if ((mlfi->mlfi_client_host = strdup(client_host)) == NULL) { logmsg(LOG_ERR, "%s: could not allocate memory", client_host); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* * Save client Forward-confirmed reverse DNS name (FCrDNS) or "unknown" * for Spamassassin's RDNS_NONE check. In the {client_name} macro, * Sendmail sends a reverse DNS name or IP address in square brackets * unless the client has a reverse DNS. Postfix sends a FCrDNS or * "unknown". */ client_name = smfi_getsymval(ctx, "{client_name}"); if (client_name == NULL || *client_name == '\0') { /* Use client_host if macro {client_name} is not set */ client_name = client_host; } if (*client_name == '[') { /* Client hostname contains IP address in square brackets */ client_name = "unknown"; } client_resolve = smfi_getsymval(ctx, "{client_resolve}"); if (client_resolve != NULL && strcmp(client_resolve, "OK") != 0) { /* If client_resolve is not "OK", the client_name is not FCrDNS */ client_name = "unknown"; } logqidmsg(mlfi, LOG_DEBUG, "client name: %s", client_name); if ((mlfi->mlfi_client_name = strdup(client_name)) == NULL) { logqidmsg(mlfi, LOG_ERR, "could not allocate memory"); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* Save client IP address */ addr = NULL; if (hostaddr != NULL) { switch(hostaddr->sa_family) { case AF_INET: addr = &((struct sockaddr_in *)hostaddr)->sin_addr; len = INET_ADDRSTRLEN; prefix = NULL; plen = 0; /* prefix length */ break; #if HAVE_DECL_AF_INET6 && HAVE_DECL_INET6_ADDRSTRLEN && HAVE_STRUCT_SOCKADDR_IN6 case AF_INET6: addr = &((struct sockaddr_in6 *)hostaddr)->sin6_addr; len = INET6_ADDRSTRLEN; prefix = "IPv6:"; plen = 5; /* prefix length */ break; #endif default: logqidmsg(mlfi, LOG_WARNING, "unrecognized address family %d for " "host %s", (int)hostaddr->sa_family, client_host); break; } } if (addr != NULL) { if ((mlfi->mlfi_client_addr = malloc(len + plen)) == NULL) { logqidmsg(mlfi, LOG_ERR, "could not allocate memory"); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } if (prefix != NULL) { (void) strlcpy(mlfi->mlfi_client_addr, prefix, len + plen); } if (inet_ntop(hostaddr->sa_family, addr, mlfi->mlfi_client_addr + plen, len) == NULL) { free(mlfi->mlfi_client_addr); mlfi->mlfi_client_addr = NULL; logqidmsg(mlfi, LOG_WARNING, "could not convert host address to " "string for host %s", client_host); } else { logqidmsg(mlfi, LOG_DEBUG, "host address: %s", mlfi->mlfi_client_addr); } } /* Save daemon name */ if ((daemon_name = smfi_getsymval(ctx, "{daemon_name}")) != NULL) { logqidmsg(mlfi, LOG_INFO, "Daemon name: %s", daemon_name); if ((mlfi->mlfi_daemon_name = strdup(daemon_name)) == NULL) { logqidmsg(mlfi, LOG_ERR, "could not allocate memory"); mlfi_setreply_tempfail(ctx); mlfi_cleanup(mlfi); return SMFIS_TEMPFAIL; } } /* Allocate amavisd communication buffer */ mlfi->mlfi_amabuf_length = AMABUFCHUNK; if ((mlfi->mlfi_amabuf = malloc(mlfi->mlfi_amabuf_length)) == NULL) { logqidmsg(mlfi, LOG_ERR, "could not allocate amavisd communication buffer"); mlfi_setreply_tempfail(ctx); mlfi_cleanup(mlfi); return SMFIS_TEMPFAIL; } /* Save private data */ if (smfi_setpriv(ctx, mlfi) != MI_SUCCESS) { logqidmsg(mlfi, LOG_ERR, "could not set milter context"); mlfi_setreply_tempfail(ctx); mlfi_cleanup(mlfi); return SMFIS_TEMPFAIL; } /* Save hostname */ if ((hostname = smfi_getsymval(ctx, "j")) != NULL) { if ((mlfi->mlfi_hostname = strdup(hostname)) == NULL) { logqidmsg(mlfi, LOG_ERR, "could not allocate memory"); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } } /* Continue processing */ return SMFIS_CONTINUE; } /* ** MLFI_HELO - Handle the HELO/EHLO command ** ** mlfi_helo() is called whenever the client sends a HELO/EHLO command. ** It may therefore be called between zero and three times. */ sfsistat mlfi_helo(SMFICTX *ctx, char* helohost) { struct mlfiCtx *mlfi = MLFICTX(ctx); /* Check milter private data */ if (mlfi == NULL) { logqidmsg(mlfi, LOG_ERR, "mlfi_helo: context is not set"); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } logqidmsg(mlfi, LOG_DEBUG, "HELO: %s", helohost); /* Save helo hostname */ if (helohost != NULL && *helohost != '\0') { free(mlfi->mlfi_helo); if ((mlfi->mlfi_helo = strdup(helohost)) == NULL) { logqidmsg(mlfi, LOG_ERR, "could not allocate memory"); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } } /* Continue processing */ return SMFIS_CONTINUE; } /* ** MLFI_ENVFORM - Handle the envelope FROM command ** ** mlfi_envfrom() is called once at the beginning of each message, before ** mlfi_envrcpt() */ sfsistat mlfi_envfrom(SMFICTX *ctx, char **envfrom) { struct mlfiCtx *mlfi = MLFICTX(ctx); char buf[64]; const char *auth_type, *auth_authen, *auth_ssf; const char *date, *qid, *wrkdir; const char *from; const char *protocol = NULL; const char *daemon_name; size_t l; time_t t; struct tm gt, lt; int gmtoff; /* Check milter private data */ if (mlfi == NULL) { logqidmsg(mlfi, LOG_ERR, "mlfi_envfrom: context is not set"); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* Save communication protocol name */ /* XXX: sendmail's macro $r is not available in mlfi_helo stage */ if (mlfi->mlfi_protocol == NULL && (protocol = smfi_getsymval(ctx, "r")) != NULL) { logqidmsg(mlfi, LOG_DEBUG, "protocol: %s", protocol); if ((mlfi->mlfi_protocol = strdup(protocol)) == NULL) { logqidmsg(mlfi, LOG_ERR, "could not allocate memory"); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } } /* Cleanup message data */ mlfi_cleanup_message(mlfi); /* Save queue id */ if ((qid = smfi_getsymval(ctx, "i")) != NULL) { if ((mlfi->mlfi_qid = strdup(qid)) == NULL) { logqidmsg(mlfi, LOG_ERR, "could not allocate memory"); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } } /* An empty sender must always be enclosed in angle brackets */ if (*envfrom != NULL && **envfrom != '\0') { from = *envfrom; } else { from = "<>"; } logqidmsg(mlfi, LOG_DEBUG, "MAIL FROM: %s", from); /* Save from mail address */ if ((mlfi->mlfi_from = strdup(from)) == NULL) { logqidmsg(mlfi, LOG_ERR, "could not allocate memory"); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* Create working directory */ if (mlfi->mlfi_qid != NULL) { (void) snprintf(mlfi->mlfi_wrkdir, sizeof(mlfi->mlfi_wrkdir) - 1, "%s/af%s", working_dir, mlfi->mlfi_qid); if (mkdir(mlfi->mlfi_wrkdir, S_IRWXU|S_IRGRP|S_IXGRP) != 0) { mlfi->mlfi_wrkdir[0] = '\0'; } } if (mlfi->mlfi_wrkdir[0] == '\0') { (void) snprintf(mlfi->mlfi_wrkdir, sizeof(mlfi->mlfi_wrkdir) - 1, "%s/afXXXXXXXXXX", working_dir); if ((wrkdir = mkdtemp(mlfi->mlfi_wrkdir)) != NULL) { (void) strlcpy(mlfi->mlfi_wrkdir, wrkdir, sizeof(mlfi->mlfi_wrkdir)); } else { logqidmsg(mlfi, LOG_ERR, "could not create working directory: %s", strerror(errno)); mlfi->mlfi_wrkdir[0] = '\0'; mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } if (chmod(mlfi->mlfi_wrkdir, S_IRWXU|S_IRGRP|S_IXGRP) == -1) { logqidmsg(mlfi, LOG_ERR, "could not change mode of directory %s: %s", mlfi->mlfi_wrkdir, strerror(errno)); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } } logqidmsg(mlfi, LOG_DEBUG, "create working directory %s", mlfi->mlfi_wrkdir); /* Open file to store this message */ (void) snprintf(mlfi->mlfi_fname, sizeof(mlfi->mlfi_fname) - 1, "%s/email.txt", mlfi->mlfi_wrkdir); if ((mlfi->mlfi_fp = fopen(mlfi->mlfi_fname, "w+")) == NULL) { logqidmsg(mlfi, LOG_ERR, "could not create message file %s: %s", mlfi->mlfi_fname, strerror(errno)); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } if (fchmod(fileno(mlfi->mlfi_fp), S_IRUSR|S_IWUSR|S_IRGRP) == -1) { logqidmsg(mlfi, LOG_ERR, "could not change mode of file %s: %s", mlfi->mlfi_fname, strerror(errno)); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } logqidmsg(mlfi, LOG_DEBUG, "create message file %s", mlfi->mlfi_fname); /* Get transaction date */ if ((date = smfi_getsymval(ctx, "b")) == NULL) { (void) time(&t); gt = *gmtime(&t); lt = *localtime(&t); gmtoff = (lt.tm_hour - gt.tm_hour) * 60 + lt.tm_min - gt.tm_min; if (lt.tm_year < gt.tm_year) { gmtoff -= 24 * 60; } else if (lt.tm_year > gt.tm_year) { gmtoff += 24 * 60; } else if (lt.tm_yday < gt.tm_yday) { gmtoff -= 24 * 60; } else if (lt.tm_yday > gt.tm_yday) { gmtoff += 24 * 60; } if (lt.tm_sec <= gt.tm_sec - 60) { gmtoff -= 1; } else if (lt.tm_sec >= gt.tm_sec + 60) { gmtoff += 1; } if (snprintf(buf, sizeof(buf), #ifdef HAVE_STRUCT_TM_TM_ZONE "%s, %d %s %d %02d:%02d:%02d %+03d%02d (%s)", #else "%s, %d %s %d %02d:%02d:%02d %+03d%02d", #endif days[lt.tm_wday], lt.tm_mday, months[lt.tm_mon], lt.tm_year + 1900, lt.tm_hour, lt.tm_min, lt.tm_sec, (int) gmtoff / 60, (int) abs(gmtoff) % 60 #ifdef HAVE_STRUCT_TM_TM_ZONE , lt.tm_zone #endif ) < sizeof(buf)) { date = buf; } } /* Write synthesized received header to the file as the first header:*/ /* Received: from ( []) (authenticated bits=)*/ /* by ( []) */ /* with (authenticated as ) id ; */ /* */ /* (envelope-from ) */ *mlfi->mlfi_amabuf = '\0'; l = snprintfcat(0, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, "Received: from %s", mlfi->mlfi_helo != NULL && *mlfi->mlfi_helo != '\0' ? mlfi->mlfi_helo : "unknown"); if ((mlfi->mlfi_client_name != NULL && *mlfi->mlfi_client_name != '\0') || (mlfi->mlfi_client_addr != NULL && *mlfi->mlfi_client_addr != '\0')) { l = snprintfcat(l, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, " ("); if (mlfi->mlfi_client_name != NULL && *mlfi->mlfi_client_name != '\0') { l = snprintfcat(l, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, "%s", mlfi->mlfi_client_name); } l = snprintfcat(l, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, " "); if (mlfi->mlfi_client_addr != NULL && *mlfi->mlfi_client_addr != '\0') { l = snprintfcat(l, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, "[%s]", mlfi->mlfi_client_addr); } l = snprintfcat(l, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, ")"); } if ((auth_type = smfi_getsymval(ctx, "{auth_type}")) != NULL) { l = snprintfcat(l, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, " (authenticated"); auth_ssf = smfi_getsymval(ctx, "{auth_ssf}"); if (auth_ssf != NULL && *auth_ssf != '\0') { l = snprintfcat(l, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, " bits=%s", auth_ssf); } l = snprintfcat(l, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, ")"); } l = snprintfcat(l, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, "\n\tby %s (" PACKAGE ")", mlfi->mlfi_hostname != NULL && *mlfi->mlfi_hostname != '\0' ? mlfi->mlfi_hostname : "localhost"); if (mlfi->mlfi_protocol != NULL && *mlfi->mlfi_protocol != '\0') { l = snprintfcat(l, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, " with %s", mlfi->mlfi_protocol); } auth_authen = smfi_getsymval(ctx, "{auth_authen}"); if (auth_type != NULL && auth_authen != NULL && *auth_authen != '\0' ) { l = snprintfcat(l, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, " (authenticated as %s)", auth_authen); } if (mlfi->mlfi_qid != NULL && *mlfi->mlfi_qid != '\0') { l = snprintfcat(l, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, " id %s", mlfi->mlfi_qid); } l = snprintfcat(l, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, ";\n"); if (date != NULL && *date != '\0') { l = snprintfcat(l, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, "\t%s\n", date); } l = snprintfcat(l, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, "\t(envelope-from %s)\n", mlfi->mlfi_from); logqidmsg(mlfi, LOG_DEBUG, "ADDHDR: %s", mlfi->mlfi_amabuf); (void) fputs(mlfi->mlfi_amabuf, mlfi->mlfi_fp); if (ferror(mlfi->mlfi_fp)) { logqidmsg(mlfi, LOG_ERR, "could not write to message file %s: %s", mlfi->mlfi_fname, strerror(errno)); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* Daemon name */ if (mlfi->mlfi_daemon_name == NULL) { if ((daemon_name = smfi_getsymval(ctx, "{daemon_name}")) != NULL) { logqidmsg(mlfi, LOG_INFO, "Daemon name: %s", daemon_name); if ((mlfi->mlfi_daemon_name = strdup(daemon_name)) == NULL) { logqidmsg(mlfi, LOG_ERR, "could not allocate memory"); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } } } /* Policy bank names */ l = 0; *mlfi->mlfi_amabuf = '\0'; if ((policybank_from_daemon_name == 1) && (mlfi->mlfi_daemon_name != NULL)) { l += snprintfcat(l, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, "%s", mlfi->mlfi_daemon_name); } if (auth_type != NULL) { if (l > 0) { l += snprintfcat(l, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, ","); } l += snprintfcat(l, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, "SMTP_AUTH,SMTP_AUTH_%s", auth_type); if (auth_ssf != NULL && *auth_ssf != '\0') { l += snprintfcat(l, mlfi->mlfi_amabuf, mlfi->mlfi_amabuf_length, ",SMTP_AUTH_%s_%s", auth_type, auth_ssf); } } if (l > 0) { if ((mlfi->mlfi_policy_bank = strdup(mlfi->mlfi_amabuf)) == NULL) { logqidmsg(mlfi, LOG_ERR, "could not allocate memory"); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } } /* Continue processing */ return SMFIS_CONTINUE; } /* ** MLFI_ENVRCPT - Handle the envelope RCPT command ** ** mlfi_envrcpt() is called once per recipient, hence one or more times ** per message, immediately after mlfi_envfrom() */ sfsistat mlfi_envrcpt(SMFICTX *ctx, char **envrcpt) { struct mlfiCtx *mlfi = MLFICTX(ctx); struct mlfiAddress *rcpt, *r; int rcptlen; /* Check milter private data */ if (mlfi == NULL) { logqidmsg(mlfi, LOG_ERR, "mlfi_envrcpt: context is not set"); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } logqidmsg(mlfi, LOG_DEBUG, "RCPT TO: %s", *envrcpt); /* Store recipient address */ rcptlen = strlen(*envrcpt); if ((rcpt = malloc(sizeof(*rcpt) + rcptlen)) == NULL) { logqidmsg(mlfi, LOG_ERR, "could not allocate memory"); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } (void) strlcpy(rcpt->q_paddr, *envrcpt, rcptlen + 1); rcpt->q_next = NULL; if (mlfi->mlfi_rcpt == NULL) { mlfi->mlfi_rcpt = rcpt; } else { r = mlfi->mlfi_rcpt; while (r->q_next != NULL) { r = r->q_next; } r->q_next = rcpt; } /* Continue processing */ return SMFIS_CONTINUE; } /* ** MLFI_HEADER - Handle a message header ** ** mlfi_header() is called zero or more times between mlfi_envrcpt() and ** mlfi_eoh(), once per message header */ sfsistat mlfi_header(SMFICTX *ctx, char *headerf, char *headerv) { struct mlfiCtx *mlfi = MLFICTX(ctx); /* Check milter private data */ if (mlfi == NULL) { logqidmsg(mlfi, LOG_ERR, "mlfi_header: context is not set"); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } logqidmsg(mlfi, LOG_DEBUG, "HEADER: %s: %s", headerf, headerv); /* Write the header to the message file */ (void) fprintf(mlfi->mlfi_fp, "%s: %s\n", headerf, headerv); if (ferror(mlfi->mlfi_fp)) { logqidmsg(mlfi, LOG_ERR, "could not write to message file %s: %s", mlfi->mlfi_fname, strerror(errno)); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* Continue processing */ return SMFIS_CONTINUE; } /* ** MLFI_EOH - Handle the end of message headers ** ** mlfi_eoh() is called once after all headers have been sent and processed */ sfsistat mlfi_eoh(SMFICTX *ctx) { struct mlfiCtx *mlfi = MLFICTX(ctx); /* Check milter private data */ if (mlfi == NULL) { logqidmsg(mlfi, LOG_ERR, "mlfi_eoh: context is not set"); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } logqidmsg(mlfi, LOG_DEBUG, "MESSAGE BODY"); /* Write the blank line between the header and the body */ (void) fprintf(mlfi->mlfi_fp, "\n"); if (ferror(mlfi->mlfi_fp)) { logqidmsg(mlfi, LOG_ERR, "could not write to message file %s: %s", mlfi->mlfi_fname, strerror(errno)); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* Continue processing */ return SMFIS_CONTINUE; } /* ** MLFI_BODY - Handle a piece of a message's body ** ** mlfi_body() is called zero or more times between mlfi_eoh() and mlfi_eom() */ sfsistat mlfi_body(SMFICTX *ctx, unsigned char * bodyp, size_t bodylen) { struct mlfiCtx *mlfi = MLFICTX(ctx); unsigned char *b, *c; /* Check milter private data */ if (mlfi == NULL) { logqidmsg(mlfi, LOG_ERR, "mlfi_body: context is not set"); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } logqidmsg(mlfi, LOG_DEBUG, "body chunk: %ld", (long)bodylen); /* Check if previous chunk ends with CR */ if (mlfi->mlfi_cr_flag != 0) { mlfi->mlfi_cr_flag = 0; if (*bodyp != '\n') { (void) fprintf(mlfi->mlfi_fp, "\r"); if (ferror(mlfi->mlfi_fp)) { logqidmsg(mlfi, LOG_ERR, "could not write to message file " "%s: %s", mlfi->mlfi_fname, strerror(errno)); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } } } /* Convert CRLF to LF */ b = c = bodyp; while (c < bodyp + bodylen) { if (mlfi->mlfi_cr_flag != 0) { mlfi->mlfi_cr_flag = 0; if (*c != '\n') { *b++ = '\r'; } *b++ = *c++; } else if (*c == '\r') { mlfi->mlfi_cr_flag = 1; c++; } else { *b++ = *c++; } } bodylen = b - bodyp; logqidmsg(mlfi, LOG_DEBUG, "after CRLF to LF conversion: %ld", (long)(bodylen)); /* Write the body chunk to the message file */ if (fwrite(bodyp, bodylen, 1, mlfi->mlfi_fp) < 1) { logqidmsg(mlfi, LOG_ERR, "could not write to message file %s: %s", mlfi->mlfi_fname, strerror(errno)); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* Continue processing */ return SMFIS_CONTINUE; } /* ** MLFI_EOM - Handle the end of a message ** ** mlfi_eom() is called once after all calls to mlfi_body() ** for a given message */ sfsistat mlfi_eom(SMFICTX *ctx) { int i; char *idx, *header, *rcode, *xcode, *name, *value; const char *qid; sfsistat rstat; struct mlfiCtx *mlfi = MLFICTX(ctx); struct mlfiAddress *rcpt; struct sockaddr_un amavisd_sock; time_t start_counter; int wait_counter; /* Check milter private data */ if (mlfi == NULL) { logqidmsg(mlfi, LOG_ERR, "mlfi_eom: context is not set"); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } logqidmsg(mlfi, LOG_DEBUG, "CONTENT CHECK"); /* Close the message file */ if (mlfi->mlfi_fp == NULL) { logqidmsg(mlfi, LOG_ERR, "message file %s is not opened", mlfi->mlfi_fname); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } if (fclose(mlfi->mlfi_fp) == -1) { mlfi->mlfi_fp = NULL; logqidmsg(mlfi, LOG_ERR, "could not close message file %s: %s", mlfi->mlfi_fname, strerror(errno)); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } mlfi->mlfi_fp = NULL; logqidmsg(mlfi, LOG_DEBUG, "close message file %s", mlfi->mlfi_fname); /* Connect to amavisd */ if (max_sem != NULL) { start_counter = time(NULL); wait_counter = MIN(SMFI_PROGRESS_TRIGGER, max_wait); while (amavisd_connect(mlfi, &amavisd_sock, start_counter + wait_counter) == -1) { if (errno != AMAVISD_CONNECT_TIMEDOUT_ERRNO) { if (ignore_amavisd_error) { return SMFIS_CONTINUE; } mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } if (wait_counter >= max_wait) { logqidmsg(mlfi, LOG_WARNING, "amavisd connection is not available for %d sec, giving up", wait_counter); if (ignore_amavisd_error) { return SMFIS_CONTINUE; } mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } #ifdef HAVE_SMFI_PROGRESS logqidmsg(mlfi, LOG_DEBUG, "amavisd connection is not available for %d sec, " "triggering sendmail", wait_counter); if (smfi_progress(ctx) != MI_SUCCESS) { logqidmsg(mlfi, LOG_ERR, "could not notify MTA that an operation is still in " "progress"); if (ignore_amavisd_error) { return SMFIS_CONTINUE; } mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } #else logqidmsg(mlfi, LOG_DEBUG, "amavisd connection not available for %d sec, waiting", wait_counter); #endif wait_counter += MIN(SMFI_PROGRESS_TRIGGER, max_wait - wait_counter); } logqidmsg(mlfi, LOG_DEBUG, "got amavisd connection for %d sec", (int)(time(NULL) - start_counter)); #ifdef HAVE_SMFI_PROGRESS if (smfi_progress(ctx) != MI_SUCCESS) { logqidmsg(mlfi, LOG_ERR, "could not notify MTA that an operation is still in progress"); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } #endif } else { if (amavisd_connect(mlfi, &amavisd_sock, 0) == -1) { logqidmsg(mlfi, LOG_ERR, "could not connect to amavisd socket %s: %s", amavisd_socket, strerror(errno)); if (ignore_amavisd_error) { return SMFIS_CONTINUE; } mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } } logqidmsg(mlfi, LOG_DEBUG, "AMAVISD REQUEST"); /* AM.PDP protocol prologue */ logqidmsg(mlfi, LOG_DEBUG, "request=AM.PDP"); if (amavisd_request(mlfi, "request", "AM.PDP") == -1) { logqidmsg(mlfi, LOG_ERR, "could not write to socket %s: %s", amavisd_socket, strerror(errno)); amavisd_close(mlfi); if (ignore_amavisd_error) { return SMFIS_CONTINUE; } mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* Get queue id (Postfix does give information about */ /* the queue-number only after the RCPT-TO-phase */ if (mlfi->mlfi_qid == NULL) { if ((qid = smfi_getsymval(ctx, "i")) != NULL) { if ((mlfi->mlfi_qid = strdup(qid)) == NULL) { logqidmsg(mlfi, LOG_ERR, "could not allocate memory"); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } } } /* MTA queue id */ if (mlfi->mlfi_qid != NULL) { logqidmsg(mlfi, LOG_DEBUG, "queue_id=%s", mlfi->mlfi_qid); if (amavisd_request(mlfi, "queue_id", mlfi->mlfi_qid) == -1) { logqidmsg(mlfi, LOG_ERR, "could not write to socket %s: %s", amavisd_socket, strerror(errno)); amavisd_close(mlfi); if (ignore_amavisd_error) { return SMFIS_CONTINUE; } mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } } /* Communication protocol */ if (mlfi->mlfi_protocol != NULL) { logqidmsg(mlfi, LOG_DEBUG, "protocol_name=%s", mlfi->mlfi_protocol); if (amavisd_request(mlfi, "protocol_name", mlfi->mlfi_protocol) == -1) { logqidmsg(mlfi, LOG_ERR, "could not write to socket %s: %s", amavisd_socket, strerror(errno)); amavisd_close(mlfi); if (ignore_amavisd_error) { return SMFIS_CONTINUE; } mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } } /* Envelope sender address */ logqidmsg(mlfi, LOG_DEBUG, "sender=%s", mlfi->mlfi_from); if (amavisd_request(mlfi, "sender", mlfi->mlfi_from) == -1) { logqidmsg(mlfi, LOG_ERR, "could not write to socket %s: %s", amavisd_socket, strerror(errno)); amavisd_close(mlfi); if (ignore_amavisd_error) { return SMFIS_CONTINUE; } mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* Envelope recipient addresses */ rcpt = mlfi->mlfi_rcpt; while (rcpt != NULL) { logqidmsg(mlfi, LOG_DEBUG, "recipient=%s", rcpt->q_paddr); if (amavisd_request(mlfi, "recipient", rcpt->q_paddr) == -1) { logqidmsg(mlfi, LOG_ERR, "could not write to socket %s: %s", amavisd_socket, strerror(errno)); amavisd_close(mlfi); if (ignore_amavisd_error) { return SMFIS_CONTINUE; } mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } rcpt = rcpt->q_next; } /* Working directory */ logqidmsg(mlfi, LOG_DEBUG, "tempdir=%s", mlfi->mlfi_wrkdir); if (amavisd_request(mlfi, "tempdir", mlfi->mlfi_wrkdir) == -1) { logqidmsg(mlfi, LOG_ERR, "could not write to socket %s: %s", amavisd_socket, strerror(errno)); amavisd_close(mlfi); if (ignore_amavisd_error) { return SMFIS_CONTINUE; } mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* Who is responsible for removing the working directory */ logqidmsg(mlfi, LOG_DEBUG, "tempdir_removed_by=client"); if (amavisd_request(mlfi, "tempdir_removed_by", "client") == -1) { logqidmsg(mlfi, LOG_ERR, "could not write to socket %s: %s", amavisd_socket, strerror(errno)); amavisd_close(mlfi); if (ignore_amavisd_error) { return SMFIS_CONTINUE; } mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* File containing the original mail */ logqidmsg(mlfi, LOG_DEBUG, "mail_file=%s", mlfi->mlfi_fname); if (amavisd_request(mlfi, "mail_file", mlfi->mlfi_fname) == -1) { logqidmsg(mlfi, LOG_ERR, "could not write to socket %s: %s", amavisd_socket, strerror(errno)); amavisd_close(mlfi); if (ignore_amavisd_error) { return SMFIS_CONTINUE; } mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* Who is responsible for forwarding the message */ logqidmsg(mlfi, LOG_DEBUG, "delivery_care_of=%s", delivery_care_of); if (amavisd_request(mlfi, "delivery_care_of", delivery_care_of) == -1) { logqidmsg(mlfi, LOG_ERR, "could not write to socket %s: %s", amavisd_socket, strerror(errno)); amavisd_close(mlfi); if (ignore_amavisd_error) { return SMFIS_CONTINUE; } mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* IP address of the original SMTP client */ logqidmsg(mlfi, LOG_DEBUG, "client_address=%s", mlfi->mlfi_client_addr); if (amavisd_request(mlfi, "client_address", mlfi->mlfi_client_addr) == -1) { logqidmsg(mlfi, LOG_ERR, "could not write to socket %s: %s", amavisd_socket, strerror(errno)); amavisd_close(mlfi); if (ignore_amavisd_error) { return SMFIS_CONTINUE; } mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* DNS name of the original SMTP client */ if (mlfi->mlfi_client_host != NULL) { logqidmsg(mlfi, LOG_DEBUG, "client_name=%s", mlfi->mlfi_client_host); if (amavisd_request(mlfi, "client_name", mlfi->mlfi_client_host) == -1) { logqidmsg(mlfi, LOG_ERR, "could not write to socket %s: %s", amavisd_socket, strerror(errno)); amavisd_close(mlfi); if (ignore_amavisd_error) { return SMFIS_CONTINUE; } mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } } /* The value of the HELO or EHLO specified by the original SMTP client */ if (mlfi->mlfi_helo != NULL) { logqidmsg(mlfi, LOG_DEBUG, "helo_name=%s", mlfi->mlfi_helo); if (amavisd_request(mlfi, "helo_name", mlfi->mlfi_helo) == -1) { logqidmsg(mlfi, LOG_ERR, "could not write to socket %s: %s", amavisd_socket, strerror(errno)); amavisd_close(mlfi); if (ignore_amavisd_error) { return SMFIS_CONTINUE; } mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } } /* Policy bank names */ if (mlfi->mlfi_policy_bank != NULL) { logqidmsg(mlfi, LOG_DEBUG, "policy_bank=%s", mlfi->mlfi_policy_bank); if (amavisd_request(mlfi, "policy_bank", mlfi->mlfi_policy_bank) == -1) { logqidmsg(mlfi, LOG_ERR, "could not write to socket %s: %s", amavisd_socket, strerror(errno)); amavisd_close(mlfi); if (ignore_amavisd_error) { return SMFIS_CONTINUE; } mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } } /* End of amavisd request */ if (amavisd_request(mlfi, NULL, NULL) == -1) { logqidmsg(mlfi, LOG_ERR, "could not write to socket %s: %s", amavisd_socket, strerror(errno)); amavisd_close(mlfi); if (ignore_amavisd_error) { return SMFIS_CONTINUE; } mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } logqidmsg(mlfi, LOG_DEBUG, "AMAVISD RESPONSE"); /* Process response from amavisd */ rstat = SMFIS_TEMPFAIL; while (amavisd_response(mlfi) != -1) { name = mlfi->mlfi_amabuf; /* Last response */ if (*name == '\0') { amavisd_close(mlfi); return rstat; } /* Get name and value */ /* = */ if ((value = strchr(name, '=')) == NULL) { logqidmsg(mlfi, LOG_ERR, "malformed line: %s", name); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } *value++ = '\0'; /* AM.PDP protocol version */ /* version_server= */ if (strcmp(name, "version_server") == 0) { logqidmsg(mlfi, LOG_DEBUG, "%s=%s", name, value); i = (int) strtol(value, &header, 10); if (header != NULL && *header != '\0') { logqidmsg(mlfi, LOG_ERR, "malformed line %s=%s", name, value); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } if (i > AMPDP_VERSION) { logqidmsg(mlfi, LOG_ERR, "incompatible AM.PDP protocol version %s", value); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* Add recipient */ /* addrcpt= */ } else if (strcmp(name, "addrcpt") == 0) { logqidmsg(mlfi, LOG_INFO, "%s=%s", name, value); if (smfi_addrcpt(ctx, value) != MI_SUCCESS) { logqidmsg(mlfi, LOG_ERR, "could not add recipient %s", value); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* Delete recipient */ /* delrcpt= */ } else if (strcmp(name, "delrcpt") == 0) { logqidmsg(mlfi, LOG_INFO, "%s=%s", name, value); if (smfi_delrcpt(ctx, value) != MI_SUCCESS) { logqidmsg(mlfi, LOG_ERR, "could not delete recipient %s", value); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* Append header */ /* addheader=
*/ } else if (strcmp(name, "addheader") == 0) { logqidmsg(mlfi, LOG_INFO, "%s=%s", name, value); header = value; if ((value = strchr(header, ' ')) == NULL) { logqidmsg(mlfi, LOG_ERR, "malformed line: %s=%s", name, header); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } *value++ = '\0'; #ifdef HAVE_SMFI_INSHEADER if (smfi_insheader(ctx, INT_MAX, header, value) != MI_SUCCESS) { #else if (smfi_addheader(ctx, header, value) != MI_SUCCESS) { #endif logqidmsg(mlfi, LOG_ERR, "could not append header %s: %s", header, value); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* Insert header */ /* insheader=
*/ } else if (strcmp(name, "insheader") == 0) { logqidmsg(mlfi, LOG_INFO, "%s=%s", name, value); idx = value; if ((value = strchr(idx, ' ')) == NULL) { logqidmsg(mlfi, LOG_ERR, "malformed line: %s=%s", name, idx); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } *value++ = '\0'; i = (int) strtol(idx, &header, 10); if (header != NULL && *header != '\0') { logqidmsg(mlfi, LOG_ERR, "malformed line %s=%s %s", name, idx, value); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } header = value; if ((value = strchr(header, ' ')) == NULL) { logqidmsg(mlfi, LOG_ERR, "malformed line: %s=%s %s", name, idx, header); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } *value++ = '\0'; #ifdef HAVE_SMFI_INSHEADER if (smfi_insheader(ctx, i, header, value) != MI_SUCCESS) { #else if (smfi_addheader(ctx, header, value) != MI_SUCCESS) { #endif logqidmsg(mlfi, LOG_ERR, "could not insert header %s %s: %s", idx, header, value); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* Change header */ /* chgheader=
*/ } else if (strcmp(name, "chgheader") == 0) { logqidmsg(mlfi, LOG_INFO, "%s=%s", name, value); idx = value; if ((value = strchr(idx, ' ')) == NULL) { logqidmsg(mlfi, LOG_ERR, "malformed line: %s=%s", name, idx); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } *value++ = '\0'; i = (int) strtol(idx, &header, 10); if (header != NULL && *header != '\0') { logqidmsg(mlfi, LOG_ERR, "malformed line %s=%s %s", name, idx, value); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } header = value; if ((value = strchr(header, ' ')) == NULL) { logqidmsg(mlfi, LOG_ERR, "malformed line: %s=%s %s", name, idx, header); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } *value++ = '\0'; if (smfi_chgheader(ctx, header, i, value) != MI_SUCCESS) { logqidmsg(mlfi, LOG_ERR, "could not change header %s %s: %s", idx, header, value); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* Delete header */ /* delheader=
*/ } else if (strcmp(name, "delheader") == 0) { logqidmsg(mlfi, LOG_INFO, "%s=%s", name, value); idx = value; if ((value = strchr(idx, ' ')) == NULL) { logqidmsg(mlfi, LOG_ERR, "malformed line: %s=%s", name, idx); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } *value++ = '\0'; i = (int) strtol(idx, &header, 10); if (header != NULL && *header != '\0') { logqidmsg(mlfi, LOG_ERR, "malformed line %s=%s %s", name, idx, value); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } if (smfi_chgheader(ctx, value, i, NULL) != MI_SUCCESS) { logqidmsg(mlfi, LOG_ERR, "could not delete header %s %s:", idx, value); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } #ifdef HAVE_SMFI_QUARANTINE /* Quarantine message */ /* quarantine= */ } else if (strcmp(name, "quarantine") == 0) { logqidmsg(mlfi, LOG_INFO, "%s=%s", name, value); if (smfi_quarantine(ctx, value) != MI_SUCCESS) { logqidmsg(mlfi, LOG_ERR, "could not quarantine message (%s)", value); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } #endif /* Set response code */ /* return_value= */ } else if (strcmp(name, "return_value") == 0) { logqidmsg(mlfi, LOG_NOTICE, "%s=%s", name, value); if (strcmp(value, "continue") == 0) { rstat = SMFIS_CONTINUE; } else if (strcmp(value, "accept") == 0) { rstat = SMFIS_ACCEPT; } else if (strcmp(value, "reject") == 0) { rstat = SMFIS_REJECT; } else if (strcmp(value, "discard") == 0) { rstat = SMFIS_DISCARD; } else if (strcmp(value, "tempfail") == 0) { rstat = SMFIS_TEMPFAIL; } else { logqidmsg(mlfi, LOG_ERR, "unknown return value %s", value); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* Set SMTP reply */ /* setreply= */ } else if (strcmp(name, "setreply") == 0) { rcode = value; if ((value = strchr(rcode, ' ')) == NULL) { logqidmsg(mlfi, LOG_ERR, "malformed line: %s=%s", name, rcode); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } *value++ = '\0'; xcode = value; if ((value = strchr(xcode, ' ')) == NULL) { logqidmsg(mlfi, LOG_ERR, "malformed line: %s=%s %s", name, rcode, xcode); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } *value++ = '\0'; /* smfi_setreply accept only 4xx and 5XX codes */ if (*rcode == '4' || *rcode == '5') { logqidmsg(mlfi, LOG_INFO, "%s=%s %s %s", name, rcode, xcode, value); if (smfi_setreply(ctx, rcode, xcode, value) != MI_SUCCESS) { logqidmsg(mlfi, LOG_ERR, "could not set reply %s %s %s", rcode, xcode, value); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } } else { logqidmsg(mlfi, LOG_DEBUG, "%s=%s %s %s", name, rcode, xcode, value); } /* Amavisd log id */ /* log_id= */ } else if (strcmp(name, "log_id") == 0) { logqidmsg(mlfi, LOG_NOTICE, "%s=%s", name, value); /* Exit code */ /* exit_code= */ } else if (strcmp(name, "exit_code") == 0) { /* ignore legacy exit_code */ logqidmsg(mlfi, LOG_DEBUG, "%s=%s", name, value); /* Unknown response */ } else { logqidmsg(mlfi, LOG_ERR, "unknown amavisd response %s=%s", name, value); amavisd_close(mlfi); mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } } /* Amavisd response fail */ logqidmsg(mlfi, LOG_ERR, "could not read from amavisd socket %s: %s", amavisd_socket, strerror(errno)); logqidmsg(mlfi, LOG_DEBUG, "amavisd response line %s", mlfi->mlfi_amabuf); amavisd_close(mlfi); if (ignore_amavisd_error) { return SMFIS_CONTINUE; } mlfi_setreply_tempfail(ctx); return SMFIS_TEMPFAIL; } /* ** MLFI_ABORT - Handle the current message's being aborted ** ** mlfi_abort() must reclaim any resources allocated on a per-message ** basis, and must be tolerant of being called between any two ** message-oriented callbacks */ sfsistat mlfi_abort(SMFICTX *ctx) { struct mlfiCtx *mlfi = MLFICTX(ctx); logqidmsg(mlfi, LOG_DEBUG, "ABORT"); /* Check milter private data */ if (mlfi == NULL) { logqidmsg(mlfi, LOG_DEBUG, "mlfi_abort: context is not set"); return SMFIS_CONTINUE; } /* Cleanup message data */ mlfi_cleanup_message(mlfi); /* Continue processing */ return SMFIS_CONTINUE; } /* ** MLFI_CLOSE - The current connection is being closed ** ** mlfi_close() is always called once at the end of each connection */ sfsistat mlfi_close(SMFICTX *ctx) { struct mlfiCtx *mlfi = MLFICTX(ctx); logqidmsg(mlfi, LOG_DEBUG, "CLOSE"); /* Check milter private data */ if (mlfi == NULL) { logqidmsg(mlfi, LOG_DEBUG, "mlfi_close: context is not set"); return SMFIS_CONTINUE; } /* Release private data */ mlfi_cleanup(mlfi); if (smfi_setpriv(ctx, NULL) != MI_SUCCESS) { /* NOTE: smfi_setpriv return MI_FAILURE when ctx is NULL */ /* logqidmsg(NULL, LOG_ERR, "could not release milter context"); */ } /* Continue processing */ return SMFIS_CONTINUE; } amavisd-milter-1.7.1/autoconf.sh.in000066400000000000000000000001311372525560500172220ustar00rootroot00000000000000#!/bin/sh aclocal -I aclocal && autoheader && autoconf && automake --add-missing --copy amavisd-milter-1.7.1/compat/000077500000000000000000000000001372525560500157335ustar00rootroot00000000000000amavisd-milter-1.7.1/compat/.editorconfig000066400000000000000000000002421372525560500204060ustar00rootroot00000000000000# Tab indentation [*.{h,c}] indent_size = 8 indent_style = tab # 4 spaces indentation [{compat.h,read_sock.c,write_sock.c}] indent_size = 4 indent_style = space amavisd-milter-1.7.1/compat/.gitignore000066400000000000000000000000331372525560500177170ustar00rootroot00000000000000.deps Makefile Makefile.in amavisd-milter-1.7.1/compat/Makefile.am000066400000000000000000000006051372525560500177700ustar00rootroot00000000000000# Distribution files EXTRA_DIST= \ .editorconfig \ Makefile.am \ Makefile.in # Header files noinst_HEADERS= \ compat.h \ fts_compat.h # Libraries noinst_LIBRARIES= \ libcompat.a # libcompat compiling parameters libcompat_a_SOURCES= \ read_sock.c \ write_sock.c libcompat_a_LIBADD= \ @LIBOBJS@ libcompat_a_DEPENDENCIES= \ @LIBOBJS@ maintainer-clean-local: -rm -f Makefile.in amavisd-milter-1.7.1/compat/compat.h000066400000000000000000000072531372525560500173760ustar00rootroot00000000000000/* * Copyright (c) 2005, Petr Rehor . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _AMAVISD_COMPAT_H #define _AMAVISD_COMPAT_H #ifdef HAVE_CONFIG_H # include #endif #if HAVE_STDBOOL_H # include #else # if ! HAVE__BOOL # ifdef __cplusplus typedef bool _Bool; # else typedef unsigned char _Bool; # endif # endif # define bool _Bool # define false 0 # define true 1 # define __bool_true_false_are_defined 1 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_DIRENT_H # include #else # define dirent direct # if HAVE_SYS_NDIR_H # include # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # endif #endif #if !defined(HAVE_DIRFD) && !defined(HAVE_DIRFD_AS_MACRO) # if defined(HAVE_DIR_D_FD) # define dirfd(_d) ((_d)->d_fd) # elif defined(HAVE_DIR_DD_FD) # define dirfd(_d) ((_d)->dd_fd) # elif defined(HAVE_DIR___DD_FD) # define dirfd(_d) ((_d)->__dd_fd) # else # error cannot figure out how to turn a DIR * into a fd # endif #endif #if ! defined(MIN) # define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif #if ! defined(MAX) # define MAX(a, b) ((a) < (b) ? (b) : (a)) #endif #if ! HAVE_DAEMON # ifndef _PATH_DEVNULL # define _PATH_DEVNULL "/dev/null" # endif /* Run detached from the controlling terminal */ extern int daemon(int, int); #endif #if HAVE_FTS_H # include #else # include "fts_compat.h" #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #if ! HAVE_MKDTEMP /* Make temporary directory */ extern char *mkdtemp(char *); #endif #if ! HAVE_STRLCPY /* String copy */ extern size_t strlcpy(char *, const char *, size_t); #endif /* Secure socket handling */ extern ssize_t read_sock(int, void *, size_t, long); extern ssize_t write_sock(int, void *, size_t, long); #endif /* _AMAVISD_COMPAT_H */ amavisd-milter-1.7.1/compat/daemon.c000066400000000000000000000045011372525560500173420ustar00rootroot00000000000000/* OPENBSD ORIGINAL: lib/libc/gen/daemon.c */ /* $OpenBSD: daemon.c,v 1.6 2005/08/08 08:05:33 espie Exp $ */ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "compat.h" int daemon(int nochdir, int noclose) { int fd; switch (fork()) { case -1: return (-1); case 0: #ifdef HAVE_CYGWIN register_9x_service(); #endif break; default: #ifdef HAVE_CYGWIN /* * This sleep avoids a race condition which kills the * child process if parent is started by a NT/W2K service. */ sleep(1); #endif _exit(0); } if (setsid() == -1) return (-1); if (!nochdir) (void)chdir("/"); if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { (void)dup2(fd, STDIN_FILENO); (void)dup2(fd, STDOUT_FILENO); (void)dup2(fd, STDERR_FILENO); if (fd > 2) (void)close (fd); } return (0); } amavisd-milter-1.7.1/compat/fts_compat.h000066400000000000000000000127321372525560500202500ustar00rootroot00000000000000/* TNFTPD ORIGINAL: libnetbsd/ftpfts.h */ /* $NetBSD: ftpfts.h,v 1.5 2008/03/09 20:54:13 lukem Exp $ */ /* from NetBSD: fts.h,v 1.17 2008/03/07 10:38:31 lukem Exp */ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)fts.h 8.3 (Berkeley) 8/14/94 */ #ifndef _FTS_H_ #define _FTS_H_ #ifndef __fts_stat_t #define __fts_stat_t struct stat #endif #ifndef __fts_nlink_t #define __fts_nlink_t nlink_t #endif #ifndef __fts_ino_t #define __fts_ino_t ino_t #endif #ifndef __fts_length_t #define __fts_length_t unsigned int #endif #ifndef __fts_number_t #define __fts_number_t int64_t #endif typedef struct { struct _ftsent *fts_cur; /* current node */ struct _ftsent *fts_child; /* linked list of children */ struct _ftsent **fts_array; /* sort array */ dev_t fts_dev; /* starting device # */ char *fts_path; /* path for this descent */ int fts_rfd; /* fd for root */ unsigned int fts_pathlen; /* sizeof(path) */ unsigned int fts_nitems; /* elements in the sort array */ int (*fts_compar) /* compare function */ (const struct _ftsent **, const struct _ftsent **); #define FTS_COMFOLLOW 0x001 /* follow command line symlinks */ #define FTS_LOGICAL 0x002 /* logical walk */ #define FTS_NOCHDIR 0x004 /* don't change directories */ #define FTS_NOSTAT 0x008 /* don't get stat info */ #define FTS_PHYSICAL 0x010 /* physical walk */ #define FTS_SEEDOT 0x020 /* return dot and dot-dot */ #define FTS_XDEV 0x040 /* don't cross devices */ #define FTS_WHITEOUT 0x080 /* return whiteout information */ #define FTS_OPTIONMASK 0x0ff /* valid user option mask */ #define FTS_NAMEONLY 0x100 /* (private) child names only */ #define FTS_STOP 0x200 /* (private) unrecoverable error */ int fts_options; /* fts_open options, global flags */ } FTS; typedef struct _ftsent { struct _ftsent *fts_cycle; /* cycle node */ struct _ftsent *fts_parent; /* parent directory */ struct _ftsent *fts_link; /* next file in directory */ __fts_number_t fts_number; /* local numeric value */ void *fts_pointer; /* local address value */ char *fts_accpath; /* access path */ char *fts_path; /* root path */ int fts_errno; /* errno for this node */ int fts_symfd; /* fd for symlink */ __fts_length_t fts_pathlen; /* strlen(fts_path) */ __fts_length_t fts_namelen; /* strlen(fts_name) */ __fts_ino_t fts_ino; /* inode */ dev_t fts_dev; /* device */ __fts_nlink_t fts_nlink; /* link count */ #define FTS_ROOTPARENTLEVEL -1 #define FTS_ROOTLEVEL 0 short fts_level; /* depth (-1 to N) */ #define FTS_D 1 /* preorder directory */ #define FTS_DC 2 /* directory that causes cycles */ #define FTS_DEFAULT 3 /* none of the above */ #define FTS_DNR 4 /* unreadable directory */ #define FTS_DOT 5 /* dot or dot-dot */ #define FTS_DP 6 /* postorder directory */ #define FTS_ERR 7 /* error; errno is set */ #define FTS_F 8 /* regular file */ #define FTS_INIT 9 /* initialized only */ #define FTS_NS 10 /* stat(2) failed */ #define FTS_NSOK 11 /* no stat(2) requested */ #define FTS_SL 12 /* symbolic link */ #define FTS_SLNONE 13 /* symbolic link without target */ #define FTS_W 14 /* whiteout object */ unsigned short fts_info; /* user flags for FTSENT structure */ #define FTS_DONTCHDIR 0x01 /* don't chdir .. to the parent */ #define FTS_SYMFOLLOW 0x02 /* followed a symlink to get here */ #define FTS_ISW 0x04 /* this is a whiteout object */ unsigned short fts_flags; /* private flags for FTSENT structure */ #define FTS_AGAIN 1 /* read node again */ #define FTS_FOLLOW 2 /* follow symbolic link */ #define FTS_NOINSTR 3 /* no instructions */ #define FTS_SKIP 4 /* discard node */ unsigned short fts_instr; /* fts_set() instructions */ __fts_stat_t *fts_statp; /* stat(2) information */ char fts_name[1]; /* file name */ } FTSENT; FTSENT *fts_children(FTS *, int); int fts_close(FTS *); FTS *fts_open(char * const *, int, int (*)(const FTSENT **, const FTSENT **)); FTSENT *fts_read(FTS *); int fts_set(FTS *, FTSENT *, int); #endif /* !_FTS_H_ */ amavisd-milter-1.7.1/compat/fts_open.c000066400000000000000000000733651372525560500177320ustar00rootroot00000000000000/* TNFTPD ORIGINAL: libnetbsd/fts_open.c */ /* $NetBSD: fts_open.c,v 1.10 2008/09/27 15:14:29 lukem Exp $ */ /* from NetBSD: fts.c,v 1.34 2008/09/27 15:12:00 lukem Exp */ /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "compat.h" static FTSENT *fts_alloc(FTS *, const char *, size_t); static FTSENT *fts_build(FTS *, int); static void fts_free(FTSENT *); static void fts_lfree(FTSENT *); static void fts_load(FTS *, FTSENT *); static size_t fts_maxarglen(char * const *); static size_t fts_pow2(size_t); static int fts_palloc(FTS *, size_t); static void fts_padjust(FTS *, FTSENT *); static FTSENT *fts_sort(FTS *, FTSENT *, size_t); static unsigned short fts_stat(FTS *, FTSENT *, int); static int fts_safe_changedir(const FTS *, const FTSENT *, int, const char *); #if defined(ALIGNBYTES) && defined(ALIGN) #define FTS_ALLOC_ALIGNED 1 #else #undef FTS_ALLOC_ALIGNED #endif #define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) #define CLR(opt) (sp->fts_options &= ~(opt)) #define ISSET(opt) (sp->fts_options & (opt)) #define SET(opt) (sp->fts_options |= (opt)) #define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path)) #define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) /* fts_build flags */ #define BCHILD 1 /* fts_children */ #define BNAMES 2 /* fts_children, names only */ #define BREAD 3 /* fts_read */ #ifndef DTF_HIDEW #undef FTS_WHITEOUT #endif FTS * fts_open(char * const *argv, int options, int (*compar)(const FTSENT **, const FTSENT **)) { FTS *sp; FTSENT *p, *root; size_t nitems; FTSENT *parent, *tmp = NULL; /* pacify gcc */ size_t len; /* Options check. */ if (options & ~FTS_OPTIONMASK) { errno = EINVAL; return (NULL); } /* Allocate/initialize the stream */ if ((sp = malloc((unsigned int)sizeof(FTS))) == NULL) return (NULL); memset(sp, 0, sizeof(FTS)); sp->fts_compar = compar; sp->fts_options = options; /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ if (ISSET(FTS_LOGICAL)) SET(FTS_NOCHDIR); /* * Start out with 1K of path space, and enough, in any case, * to hold the user's paths. */ if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN))) goto mem1; /* Allocate/initialize root's parent. */ if ((parent = fts_alloc(sp, "", 0)) == NULL) goto mem2; parent->fts_level = FTS_ROOTPARENTLEVEL; /* Allocate/initialize root(s). */ for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { /* Don't allow zero-length paths. */ if ((len = strlen(*argv)) == 0) { errno = ENOENT; goto mem3; } if ((p = fts_alloc(sp, *argv, len)) == NULL) goto mem3; p->fts_level = FTS_ROOTLEVEL; p->fts_parent = parent; p->fts_accpath = p->fts_name; p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW)); /* Command-line "." and ".." are real directories. */ if (p->fts_info == FTS_DOT) p->fts_info = FTS_D; /* * If comparison routine supplied, traverse in sorted * order; otherwise traverse in the order specified. */ if (compar) { p->fts_link = root; root = p; } else { p->fts_link = NULL; if (root == NULL) tmp = root = p; else { tmp->fts_link = p; tmp = p; } } } if (compar && nitems > 1) root = fts_sort(sp, root, nitems); /* * Allocate a dummy pointer and make fts_read think that we've just * finished the node before the root(s); set p->fts_info to FTS_INIT * so that everything about the "current" node is ignored. */ if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) goto mem3; sp->fts_cur->fts_link = root; sp->fts_cur->fts_info = FTS_INIT; /* * If using chdir(2), grab a file descriptor pointing to dot to insure * that we can get back here; this could be avoided for some paths, * but almost certainly not worth the effort. Slashes, symbolic links, * and ".." are all fairly nasty problems. Note, if we can't get the * descriptor we run anyway, just more slowly. */ if (!ISSET(FTS_NOCHDIR)) { if ((sp->fts_rfd = open(".", O_RDONLY, 0)) == -1) SET(FTS_NOCHDIR); else if (fcntl(sp->fts_rfd, F_SETFD, FD_CLOEXEC) == -1) { close(sp->fts_rfd); SET(FTS_NOCHDIR); } } if (nitems == 0) fts_free(parent); return (sp); mem3: fts_lfree(root); fts_free(parent); mem2: free(sp->fts_path); mem1: free(sp); return (NULL); } static void fts_load(FTS *sp, FTSENT *p) { size_t len; char *cp; /* * Load the stream structure for the next traversal. Since we don't * actually enter the directory until after the preorder visit, set * the fts_accpath field specially so the chdir gets done to the right * place and the user can access the first node. From fts_open it's * known that the path will fit. */ len = p->fts_pathlen = p->fts_namelen; memmove(sp->fts_path, p->fts_name, len + 1); if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { len = strlen(++cp); memmove(p->fts_name, cp, len + 1); p->fts_namelen = len; } p->fts_accpath = p->fts_path = sp->fts_path; sp->fts_dev = p->fts_dev; } int fts_close(FTS *sp) { FTSENT *freep, *p; int saved_errno = 0; /* * This still works if we haven't read anything -- the dummy structure * points to the root list, so we step through to the end of the root * list which has a valid parent pointer. */ if (sp->fts_cur) { if (ISSET(FTS_SYMFOLLOW)) (void)close(sp->fts_cur->fts_symfd); for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { freep = p; p = p->fts_link ? p->fts_link : p->fts_parent; fts_free(freep); } fts_free(p); } /* Free up child linked list, sort array, path buffer. */ if (sp->fts_child) fts_lfree(sp->fts_child); if (sp->fts_array) free(sp->fts_array); free(sp->fts_path); /* Return to original directory, save errno if necessary. */ if (!ISSET(FTS_NOCHDIR)) { if (fchdir(sp->fts_rfd) == -1) saved_errno = errno; (void)close(sp->fts_rfd); } /* Free up the stream pointer. */ free(sp); if (saved_errno) { errno = saved_errno; return -1; } return 0; } #if !defined(__FTS_COMPAT_TAILINGSLASH) /* * Special case of "/" at the end of the path so that slashes aren't * appended which would cause paths to be written as "....//foo". */ #define NAPPEND(p) \ (p->fts_path[p->fts_pathlen - 1] == '/' \ ? p->fts_pathlen - 1 : p->fts_pathlen) #else /* !defined(__FTS_COMPAT_TAILINGSLASH) */ /* * compatibility with the old behaviour. * * Special case a root of "/" so that slashes aren't appended which would * cause paths to be written as "//foo". */ #define NAPPEND(p) \ (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \ p->fts_path[0] == '/' ? 0 : p->fts_pathlen) #endif /* !defined(__FTS_COMPAT_TAILINGSLASH) */ FTSENT * fts_read(FTS *sp) { FTSENT *p, *tmp; int instr; char *t; int saved_errno; /* If finished or unrecoverable error, return NULL. */ if (sp->fts_cur == NULL || ISSET(FTS_STOP)) return (NULL); /* Set current node pointer. */ p = sp->fts_cur; /* Save and zero out user instructions. */ instr = p->fts_instr; p->fts_instr = FTS_NOINSTR; /* Any type of file may be re-visited; re-stat and re-turn. */ if (instr == FTS_AGAIN) { p->fts_info = fts_stat(sp, p, 0); return (p); } /* * Following a symlink -- SLNONE test allows application to see * SLNONE and recover. If indirecting through a symlink, have * keep a pointer to current location. If unable to get that * pointer, follow fails. */ if (instr == FTS_FOLLOW && (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { p->fts_info = fts_stat(sp, p, 1); if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { if ((p->fts_symfd = open(".", O_RDONLY, 0)) == -1) { p->fts_errno = errno; p->fts_info = FTS_ERR; } else if (fcntl(p->fts_symfd, F_SETFD, FD_CLOEXEC) == -1) { p->fts_errno = errno; p->fts_info = FTS_ERR; close(p->fts_symfd); } else p->fts_flags |= FTS_SYMFOLLOW; } return (p); } /* Directory in pre-order. */ if (p->fts_info == FTS_D) { /* If skipped or crossed mount point, do post-order visit. */ if (instr == FTS_SKIP || (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) { if (p->fts_flags & FTS_SYMFOLLOW) (void)close(p->fts_symfd); if (sp->fts_child) { fts_lfree(sp->fts_child); sp->fts_child = NULL; } p->fts_info = FTS_DP; return (p); } /* Rebuild if only read the names and now traversing. */ if (sp->fts_child && ISSET(FTS_NAMEONLY)) { CLR(FTS_NAMEONLY); fts_lfree(sp->fts_child); sp->fts_child = NULL; } /* * Cd to the subdirectory. * * If have already read and now fail to chdir, whack the list * to make the names come out right, and set the parent errno * so the application will eventually get an error condition. * Set the FTS_DONTCHDIR flag so that when we logically change * directories back to the parent we don't do a chdir. * * If haven't read do so. If the read fails, fts_build sets * FTS_STOP or the fts_info field of the node. */ if (sp->fts_child) { if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) { p->fts_errno = errno; p->fts_flags |= FTS_DONTCHDIR; for (p = sp->fts_child; p; p = p->fts_link) p->fts_accpath = p->fts_parent->fts_accpath; } } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) { if (ISSET(FTS_STOP)) return (NULL); return (p); } p = sp->fts_child; sp->fts_child = NULL; goto name; } /* Move to the next node on this level. */ next: tmp = p; if ((p = p->fts_link) != NULL) { fts_free(tmp); /* * If reached the top, return to the original directory, and * load the paths for the next root. */ if (p->fts_level == FTS_ROOTLEVEL) { if (FCHDIR(sp, sp->fts_rfd)) { SET(FTS_STOP); return (NULL); } fts_load(sp, p); return (sp->fts_cur = p); } /* * User may have called fts_set on the node. If skipped, * ignore. If followed, get a file descriptor so we can * get back if necessary. */ if (p->fts_instr == FTS_SKIP) goto next; if (p->fts_instr == FTS_FOLLOW) { p->fts_info = fts_stat(sp, p, 1); if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { if ((p->fts_symfd = open(".", O_RDONLY, 0)) == -1) { p->fts_errno = errno; p->fts_info = FTS_ERR; } else if (fcntl(p->fts_symfd, F_SETFD, FD_CLOEXEC) == -1) { p->fts_errno = errno; p->fts_info = FTS_ERR; close(p->fts_symfd); } else p->fts_flags |= FTS_SYMFOLLOW; } p->fts_instr = FTS_NOINSTR; } name: t = sp->fts_path + NAPPEND(p->fts_parent); *t++ = '/'; memmove(t, p->fts_name, (size_t)(p->fts_namelen + 1)); return (sp->fts_cur = p); } /* Move up to the parent node. */ p = tmp->fts_parent; fts_free(tmp); if (p->fts_level == FTS_ROOTPARENTLEVEL) { /* * Done; free everything up and set errno to 0 so the user * can distinguish between error and EOF. */ fts_free(p); errno = 0; return (sp->fts_cur = NULL); } /* Nul terminate the pathname. */ sp->fts_path[p->fts_pathlen] = '\0'; /* * Return to the parent directory. If at a root node or came through * a symlink, go back through the file descriptor. Otherwise, cd up * one directory. */ if (p->fts_level == FTS_ROOTLEVEL) { if (FCHDIR(sp, sp->fts_rfd)) { SET(FTS_STOP); return (NULL); } } else if (p->fts_flags & FTS_SYMFOLLOW) { if (FCHDIR(sp, p->fts_symfd)) { saved_errno = errno; (void)close(p->fts_symfd); errno = saved_errno; SET(FTS_STOP); return (NULL); } (void)close(p->fts_symfd); } else if (!(p->fts_flags & FTS_DONTCHDIR) && fts_safe_changedir(sp, p->fts_parent, -1, "..")) { SET(FTS_STOP); return (NULL); } p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; return (sp->fts_cur = p); } /* * Fts_set takes the stream as an argument although it's not used in this * implementation; it would be necessary if anyone wanted to add global * semantics to fts using fts_set. An error return is allowed for similar * reasons. */ /* ARGSUSED */ int fts_set(FTS *sp, FTSENT *p, int instr) { if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW && instr != FTS_NOINSTR && instr != FTS_SKIP) { errno = EINVAL; return (1); } p->fts_instr = instr; return (0); } FTSENT * fts_children(FTS *sp, int instr) { FTSENT *p; int fd; if (instr && instr != FTS_NAMEONLY) { errno = EINVAL; return (NULL); } /* Set current node pointer. */ p = sp->fts_cur; /* * Errno set to 0 so user can distinguish empty directory from * an error. */ errno = 0; /* Fatal errors stop here. */ if (ISSET(FTS_STOP)) return (NULL); /* Return logical hierarchy of user's arguments. */ if (p->fts_info == FTS_INIT) return (p->fts_link); /* * If not a directory being visited in pre-order, stop here. Could * allow FTS_DNR, assuming the user has fixed the problem, but the * same effect is available with FTS_AGAIN. */ if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) return (NULL); /* Free up any previous child list. */ if (sp->fts_child) fts_lfree(sp->fts_child); if (instr == FTS_NAMEONLY) { SET(FTS_NAMEONLY); instr = BNAMES; } else instr = BCHILD; /* * If using chdir on a relative path and called BEFORE fts_read does * its chdir to the root of a traversal, we can lose -- we need to * chdir into the subdirectory, and we don't know where the current * directory is, so we can't get back so that the upcoming chdir by * fts_read will work. */ if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' || ISSET(FTS_NOCHDIR)) return (sp->fts_child = fts_build(sp, instr)); if ((fd = open(".", O_RDONLY, 0)) == -1) return (sp->fts_child = NULL); sp->fts_child = fts_build(sp, instr); if (fchdir(fd)) { (void)close(fd); return (NULL); } (void)close(fd); return (sp->fts_child); } /* * This is the tricky part -- do not casually change *anything* in here. The * idea is to build the linked list of entries that are used by fts_children * and fts_read. There are lots of special cases. * * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is * set and it's a physical walk (so that symbolic links can't be directories), * we can do things quickly. First, if it's a 4.4BSD file system, the type * of the file is in the directory entry. Otherwise, we assume that the number * of subdirectories in a node is equal to the number of links to the parent. * The former skips all stat calls. The latter skips stat calls in any leaf * directories and for any files after the subdirectories in the directory have * been found, cutting the stat calls by about 2/3. */ static FTSENT * fts_build(FTS *sp, int type) { struct dirent *dp; FTSENT *p, *head; size_t nitems; FTSENT *cur, *tail; DIR *dirp; void *oldaddr; size_t dnamlen; int cderrno, descend, len, level, nlinks, saved_errno, nostat, doadjust; size_t maxlen; #ifdef FTS_WHITEOUT int oflag; #endif char *cp = NULL; /* pacify gcc */ /* Set current node pointer. */ cur = sp->fts_cur; /* * Open the directory for reading. If this fails, we're done. * If being called from fts_read, set the fts_info field. */ #ifdef FTS_WHITEOUT if (ISSET(FTS_WHITEOUT)) oflag = DTF_NODUP|DTF_REWIND; else oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND; #else #define __opendir2(path, flag) opendir(path) #endif if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) { if (type == BREAD) { cur->fts_info = FTS_DNR; cur->fts_errno = errno; } return (NULL); } /* * Nlinks is the number of possible entries of type directory in the * directory if we're cheating on stat calls, 0 if we're not doing * any stat calls at all, -1 if we're doing stats on everything. */ if (type == BNAMES) { nlinks = 0; nostat = 1; } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) { nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); nostat = 1; } else { nlinks = -1; nostat = 0; } #ifdef notdef (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink); (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT)); #endif /* * If we're going to need to stat anything or we want to descend * and stay in the directory, chdir. If this fails we keep going, * but set a flag so we don't chdir after the post-order visit. * We won't be able to stat anything, but we can still return the * names themselves. Note, that since fts_read won't be able to * chdir into the directory, it will have to return different path * names than before, i.e. "a/b" instead of "b". Since the node * has already been visited in pre-order, have to wait until the * post-order visit to return the error. There is a special case * here, if there was nothing to stat then it's not an error to * not be able to stat. This is all fairly nasty. If a program * needed sorted entries or stat information, they had better be * checking FTS_NS on the returned nodes. */ cderrno = 0; if (nlinks || type == BREAD) { if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) { if (nlinks && type == BREAD) cur->fts_errno = errno; cur->fts_flags |= FTS_DONTCHDIR; descend = 0; cderrno = errno; } else descend = 1; } else descend = 0; /* * Figure out the max file name length that can be stored in the * current path -- the inner loop allocates more path as necessary. * We really wouldn't have to do the maxlen calculations here, we * could do them in fts_read before returning the path, but it's a * lot easier here since the length is part of the dirent structure. * * If not changing directories set a pointer so that can just append * each new name into the path. */ len = NAPPEND(cur); if (ISSET(FTS_NOCHDIR)) { cp = sp->fts_path + len; *cp++ = '/'; } len++; maxlen = sp->fts_pathlen - len; level = cur->fts_level + 1; /* Read the directory, attaching each entry to the `link' pointer. */ doadjust = 0; for (head = tail = NULL, nitems = 0; (dp = readdir(dirp)) != NULL;) { if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) continue; #if defined(HAVE_STRUCT_DIRENT_D_NAMLEN) dnamlen = dp->d_namlen; #else dnamlen = strlen(dp->d_name); #endif if ((p = fts_alloc(sp, dp->d_name, dnamlen)) == NULL) goto mem1; if (dnamlen >= maxlen) { /* include space for NUL */ oldaddr = sp->fts_path; if (fts_palloc(sp, dnamlen + len + 1)) { /* * No more memory for path or structures. Save * errno, free up the current structure and the * structures already allocated. */ mem1: saved_errno = errno; if (p) fts_free(p); fts_lfree(head); (void)closedir(dirp); errno = saved_errno; cur->fts_info = FTS_ERR; SET(FTS_STOP); return (NULL); } /* Did realloc() change the pointer? */ if (oldaddr != sp->fts_path) { doadjust = 1; if (ISSET(FTS_NOCHDIR)) cp = sp->fts_path + len; } maxlen = sp->fts_pathlen - len; } #if defined(__FTS_COMPAT_LENGTH) if (len + dnamlen >= USHRT_MAX) { /* * In an FTSENT, fts_pathlen is an unsigned short * so it is possible to wraparound here. * If we do, free up the current structure and the * structures already allocated, then error out * with ENAMETOOLONG. */ fts_free(p); fts_lfree(head); (void)closedir(dirp); cur->fts_info = FTS_ERR; SET(FTS_STOP); errno = ENAMETOOLONG; return (NULL); } #endif p->fts_level = level; p->fts_pathlen = len + dnamlen; p->fts_parent = sp->fts_cur; #ifdef FTS_WHITEOUT if (dp->d_type == DT_WHT) p->fts_flags |= FTS_ISW; #endif if (cderrno) { if (nlinks) { p->fts_info = FTS_NS; p->fts_errno = cderrno; } else p->fts_info = FTS_NSOK; p->fts_accpath = cur->fts_accpath; } else if (nlinks == 0 #ifdef DT_DIR || (nostat && dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN) #endif ) { p->fts_accpath = ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; p->fts_info = FTS_NSOK; } else { /* Build a file name for fts_stat to stat. */ if (ISSET(FTS_NOCHDIR)) { p->fts_accpath = p->fts_path; memmove(cp, p->fts_name, (size_t)(p->fts_namelen + 1)); } else p->fts_accpath = p->fts_name; /* Stat it. */ p->fts_info = fts_stat(sp, p, 0); /* Decrement link count if applicable. */ if (nlinks > 0 && (p->fts_info == FTS_D || p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) --nlinks; } /* We walk in directory order so "ls -f" doesn't get upset. */ p->fts_link = NULL; if (head == NULL) head = tail = p; else { tail->fts_link = p; tail = p; } ++nitems; } (void)closedir(dirp); /* * If had to realloc the path, adjust the addresses for the rest * of the tree. */ if (doadjust) fts_padjust(sp, head); /* * If not changing directories, reset the path back to original * state. */ if (ISSET(FTS_NOCHDIR)) { if (len == sp->fts_pathlen || nitems == 0) --cp; *cp = '\0'; } /* * If descended after called from fts_children or after called from * fts_read and nothing found, get back. At the root level we use * the saved fd; if one of fts_open()'s arguments is a relative path * to an empty directory, we wind up here with no other way back. If * can't get back, we're done. */ if (descend && (type == BCHILD || !nitems) && (cur->fts_level == FTS_ROOTLEVEL ? FCHDIR(sp, sp->fts_rfd) : fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) { cur->fts_info = FTS_ERR; SET(FTS_STOP); return (NULL); } /* If didn't find anything, return NULL. */ if (!nitems) { if (type == BREAD) cur->fts_info = FTS_DP; return (NULL); } /* Sort the entries. */ if (sp->fts_compar && nitems > 1) head = fts_sort(sp, head, nitems); return (head); } static unsigned short fts_stat(FTS *sp, FTSENT *p, int follow) { FTSENT *t; dev_t dev; __fts_ino_t ino; __fts_stat_t *sbp, sb; int saved_errno; /* If user needs stat info, stat buffer already allocated. */ sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; #ifdef FTS_WHITEOUT /* check for whiteout */ if (p->fts_flags & FTS_ISW) { if (sbp != &sb) { memset(sbp, '\0', sizeof (*sbp)); sbp->st_mode = S_IFWHT; } return (FTS_W); } #endif /* * If doing a logical walk, or application requested FTS_FOLLOW, do * a stat(2). If that fails, check for a non-existent symlink. If * fail, set the errno from the stat call. */ if (ISSET(FTS_LOGICAL) || follow) { if (stat(p->fts_accpath, sbp)) { saved_errno = errno; if (!lstat(p->fts_accpath, sbp)) { errno = 0; return (FTS_SLNONE); } p->fts_errno = saved_errno; goto err; } } else if (lstat(p->fts_accpath, sbp)) { p->fts_errno = errno; err: memset(sbp, 0, sizeof(*sbp)); return (FTS_NS); } if (S_ISDIR(sbp->st_mode)) { /* * Set the device/inode. Used to find cycles and check for * crossing mount points. Also remember the link count, used * in fts_build to limit the number of stat calls. It is * understood that these fields are only referenced if fts_info * is set to FTS_D. */ dev = p->fts_dev = sbp->st_dev; ino = p->fts_ino = sbp->st_ino; p->fts_nlink = sbp->st_nlink; if (ISDOT(p->fts_name)) return (FTS_DOT); /* * Cycle detection is done by brute force when the directory * is first encountered. If the tree gets deep enough or the * number of symbolic links to directories is high enough, * something faster might be worthwhile. */ for (t = p->fts_parent; t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) if (ino == t->fts_ino && dev == t->fts_dev) { p->fts_cycle = t; return (FTS_DC); } return (FTS_D); } if (S_ISLNK(sbp->st_mode)) return (FTS_SL); if (S_ISREG(sbp->st_mode)) return (FTS_F); return (FTS_DEFAULT); } static FTSENT * fts_sort(FTS *sp, FTSENT *head, size_t nitems) { FTSENT **ap, *p; /* * Construct an array of pointers to the structures and call qsort(3). * Reassemble the array in the order returned by qsort. If unable to * sort for memory reasons, return the directory entries in their * current order. Allocate enough space for the current needs plus * 40 so don't realloc one entry at a time. */ if (nitems > sp->fts_nitems) { FTSENT **new; new = realloc(sp->fts_array, sizeof(FTSENT *) * (nitems + 40)); if (new == 0) return (head); sp->fts_array = new; sp->fts_nitems = nitems + 40; } for (ap = sp->fts_array, p = head; p; p = p->fts_link) *ap++ = p; qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), (int (*)(const void *, const void *))sp->fts_compar); for (head = *(ap = sp->fts_array); --nitems; ++ap) ap[0]->fts_link = ap[1]; ap[0]->fts_link = NULL; return (head); } static FTSENT * fts_alloc(FTS *sp, const char *name, size_t namelen) { FTSENT *p; #if defined(FTS_ALLOC_ALIGNED) size_t len; #endif #if defined(FTS_ALLOC_ALIGNED) /* * The file name is a variable length array and no stat structure is * necessary if the user has set the nostat bit. Allocate the FTSENT * structure, the file name and the stat structure in one chunk, but * be careful that the stat structure is reasonably aligned. Since the * fts_name field is declared to be of size 1, the fts_name pointer is * namelen + 2 before the first possible address of the stat structure. */ len = sizeof(FTSENT) + namelen; if (!ISSET(FTS_NOSTAT)) len += sizeof(*(p->fts_statp)) + ALIGNBYTES; if ((p = malloc(len)) == NULL) return (NULL); if (!ISSET(FTS_NOSTAT)) p->fts_statp = (__fts_stat_t *)ALIGN( (unsigned long)(p->fts_name + namelen + 2)); #else if ((p = malloc(sizeof(FTSENT) + namelen)) == NULL) return (NULL); if (!ISSET(FTS_NOSTAT)) if ((p->fts_statp = malloc(sizeof(*(p->fts_statp)))) == NULL) { free(p); return (NULL); } #endif /* Copy the name plus the trailing NULL. */ memmove(p->fts_name, name, namelen + 1); p->fts_namelen = namelen; p->fts_path = sp->fts_path; p->fts_errno = 0; p->fts_flags = 0; p->fts_instr = FTS_NOINSTR; p->fts_number = 0; p->fts_pointer = NULL; return (p); } static void fts_free(FTSENT *p) { #if !defined(FTS_ALLOC_ALIGNED) if (p->fts_statp) free(p->fts_statp); #endif free(p); } static void fts_lfree(FTSENT *head) { FTSENT *p; /* XXX: head may be NULL ? */ /* Free a linked list of structures. */ while ((p = head) != NULL) { head = head->fts_link; fts_free(p); } } static size_t fts_pow2(size_t x) { x--; x |= x>>1; x |= x>>2; x |= x>>4; x |= x>>8; x |= x>>16; #if LONG_BIT > 32 x |= x>>32; #endif #if LONG_BIT > 64 x |= x>>64; #endif x++; return (x); } /* * Allow essentially unlimited paths; find, rm, ls should all work on any tree. * Most systems will allow creation of paths much longer than MAXPATHLEN, even * though the kernel won't resolve them. Round up the new size to a power of 2, * so we don't realloc the path 2 bytes at a time. */ static int fts_palloc(FTS *sp, size_t size) { char *new; #ifdef __FTS_COMPAT_LENGTH /* Protect against fts_pathlen overflow. */ if (size > USHRT_MAX + 1) { errno = ENAMETOOLONG; return (1); } #endif size = fts_pow2(size); new = realloc(sp->fts_path, size); if (new == 0) return (1); sp->fts_path = new; sp->fts_pathlen = size; return (0); } /* * When the path is realloc'd, have to fix all of the pointers in structures * already returned. */ static void fts_padjust(FTS *sp, FTSENT *head) { FTSENT *p; char *addr; #define ADJUST(p) do { \ if ((p)->fts_accpath != (p)->fts_name) \ (p)->fts_accpath = \ addr + ((p)->fts_accpath - (p)->fts_path); \ (p)->fts_path = addr; \ } while (/*CONSTCOND*/0) addr = sp->fts_path; /* Adjust the current set of children. */ for (p = sp->fts_child; p; p = p->fts_link) ADJUST(p); /* Adjust the rest of the tree, including the current level. */ for (p = head; p->fts_level >= FTS_ROOTLEVEL;) { ADJUST(p); p = p->fts_link ? p->fts_link : p->fts_parent; } } static size_t fts_maxarglen(char * const *argv) { size_t len, max; for (max = 0; *argv; ++argv) if ((len = strlen(*argv)) > max) max = len; return (max + 1); } /* * Change to dir specified by fd or p->fts_accpath without getting * tricked by someone changing the world out from underneath us. * Assumes p->fts_dev and p->fts_ino are filled in. */ static int fts_safe_changedir(const FTS *sp, const FTSENT *p, int fd, const char *path) { int oldfd = fd, ret = -1; __fts_stat_t sb; if (ISSET(FTS_NOCHDIR)) return 0; if (oldfd < 0 && (fd = open(path, O_RDONLY)) == -1) return -1; if (fstat(fd, &sb) == -1) goto bail; if (sb.st_ino != p->fts_ino || sb.st_dev != p->fts_dev) { errno = ENOENT; goto bail; } ret = fchdir(fd); bail: if (oldfd < 0) { int save_errno = errno; (void)close(fd); errno = save_errno; } return ret; } amavisd-milter-1.7.1/compat/mkdtemp.c000066400000000000000000000076271372525560500175540ustar00rootroot00000000000000/* OPENBSD ORIGINAL: lib/libc/stdio/mktemp.c */ /* * Copyright (c) 1987, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char rcsid[] = "$OpenBSD: mktemp.c,v 1.18 2004/09/28 18:12:44 otto Exp $"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include #include #include #include static int _gettemp(char *, int *, int, int); char * mkdtemp(char *path) { return(_gettemp(path, (int *)NULL, 1, 0) ? path : (char *)NULL); } static int _gettemp(char *path, int *doopen, int domkdir, int slen) { char *start, *trv, *suffp; struct stat sbuf; int rval; pid_t pid; if (doopen && domkdir) { errno = EINVAL; return(0); } for (trv = path; *trv; ++trv) ; trv -= slen; suffp = trv; --trv; if (trv < path) { errno = EINVAL; return (0); } pid = getpid(); while (trv >= path && *trv == 'X' && pid != 0) { *trv-- = (pid % 10) + '0'; pid /= 10; } while (trv >= path && *trv == 'X') { char c; #ifdef HAVE_ARC4RANDOM pid = (arc4random() & 0xffff) % (26+26); #else pid = (random() & 0xffff) % (26+26); #endif if (pid < 26) c = pid + 'A'; else c = (pid - 26) + 'a'; *trv-- = c; } start = trv + 1; /* * check the target directory; if you have six X's and it * doesn't exist this runs for a *very* long time. */ if (doopen || domkdir) { for (;; --trv) { if (trv <= path) break; if (*trv == '/') { *trv = '\0'; rval = stat(path, &sbuf); *trv = '/'; if (rval != 0) return(0); if (!S_ISDIR(sbuf.st_mode)) { errno = ENOTDIR; return(0); } break; } } } for (;;) { if (doopen) { if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0) return(1); if (errno != EEXIST) return(0); } else if (domkdir) { if (mkdir(path, 0700) == 0) return(1); if (errno != EEXIST) return(0); } else if (lstat(path, &sbuf)) return(errno == ENOENT ? 1 : 0); /* tricky little algorithm for backward compatibility */ for (trv = start;;) { if (!*trv) return (0); if (*trv == 'Z') { if (trv == suffp) return (0); *trv++ = 'a'; } else { if (isdigit(*trv)) *trv = 'a'; else if (*trv == 'z') /* inc from z to A */ *trv = 'A'; else { if (trv == suffp) return (0); ++*trv; } break; } } } /*NOTREACHED*/ } amavisd-milter-1.7.1/compat/read_sock.c000066400000000000000000000066141372525560500200400ustar00rootroot00000000000000/* * Copyright (c) 2005, Petr Rehor . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "compat.h" #include /* ** READ_SOCK - read N bytes from socket */ ssize_t read_sock(int sd, void *buf, size_t nbytes, long timeout) { int ret; char *b = (char *) buf; fd_set rfds, efds; size_t n = 0; ssize_t m = 0; struct timeval tv; /* Set timeout */ tv.tv_sec = timeout; tv.tv_usec = 0; /* Check socket descriptor */ if (sd >= (int) FD_SETSIZE) { /* sd is larger than FD_SETSIZE */ errno = EBADF; return -1; } /* Read N bytes from socket */ while (n < nbytes) { FD_ZERO(&rfds); FD_ZERO(&efds); FD_SET((unsigned int)sd, &rfds); FD_SET((unsigned int)sd, &efds); /* Wait for socket */ ret = select(sd + 1, &rfds, NULL, &efds, &tv); if (ret == -1) { if (errno == EINTR) { /* A signal was delivered, continue */ continue; } else { /* An error occured */ return -1; } } else if (ret == 0) { /* Timeout */ errno = ETIMEDOUT; return -1; } if (FD_ISSET(sd, &efds)) { /* Out-of-band data received on socket */ errno = EIO; return -1; } /* Read data from socket */ m = read(sd, b, nbytes - n); if (m == -1) { if (errno == EINTR) { /* A signal was delivered, continue */ continue; } else { /* An error occured */ return -1; } } else if (m == 0) { /* End of file */ nbytes = n; } else { /* Read data */ n += m; b += m; } } /* Return number of bytes */ return nbytes; } amavisd-milter-1.7.1/compat/strlcpy.c000066400000000000000000000034521372525560500176030ustar00rootroot00000000000000/* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */ /* $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "compat.h" #if defined(LIBC_SCCS) && !defined(lint) static char *rcsid = "$OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $"; #endif /* LIBC_SCCS and not lint */ #include #include /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t siz) { register char *d = dst; register const char *s = src; register size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0 && --n != 0) { do { if ((*d++ = *s++) == 0) break; } while (--n != 0); } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ } amavisd-milter-1.7.1/compat/write_sock.c000066400000000000000000000061371372525560500202570ustar00rootroot00000000000000/* * Copyright (c) 2005, Petr Rehor . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "compat.h" #include /* ** WRITE_SOCK - write N bytes to socket */ ssize_t write_sock(int sd, void *buf, size_t nbytes, long timeout) { int ret; char *b = (char *) buf; fd_set wfds; size_t n = 0; ssize_t m = 0; struct timeval tv; /* Set timeout */ tv.tv_sec = timeout; tv.tv_usec = 0; /* Check socket descriptor */ if (sd >= (int) FD_SETSIZE) { /* sd is larger than FD_SETSIZE */ errno = EBADF; return -1; } /* Write N bytes to socket */ while (n < nbytes) { FD_ZERO(&wfds); FD_SET((unsigned int)sd, &wfds); /* Wait for socket */ ret = select(sd + 1, NULL, &wfds, NULL, &tv); if (ret == -1) { if (errno == EINTR) { /* A signal was delivered, continue */ continue; } else { /* An error occured */ return -1; } } else if (ret == 0) { /* Timeout */ errno = ETIMEDOUT; return -1; } /* Write data to socket */ m = write(sd, b, nbytes - n); if (m == -1) { if (errno == EINTR) { /* A signal was delivered, continue */ continue; } else { /* An error occured */ return -1; } } else { /* Write data */ n += m; b += m; } } /* Return number of bytes */ return nbytes; } amavisd-milter-1.7.1/configure.ac000066400000000000000000000040241372525560500167360ustar00rootroot00000000000000AC_INIT([amavisd-milter], m4_esyscmd([ [ -n "${AMAVISD_MILTER_VERSION}" ] && /bin/echo -n "${AMAVISD_MILTER_VERSION}" || /bin/echo -n "dev-$(TZ=UTC date +%Y%m%d)" ])dnl ) AC_PREREQ(2.62) AC_CONFIG_SRCDIR(aclocal/acinclude.m4) AC_CONFIG_AUX_DIR(aclocal) AC_CONFIG_MACRO_DIR(aclocal) AC_CONFIG_HEADERS([config.h]) AC_CANONICAL_BUILD AC_CANONICAL_HOST AC_CANONICAL_TARGET AM_INIT_AUTOMAKE([-Wall -Werror foreign]) dnl AM_MAINTAINER_MODE # Kludge to keep autoconf from adding -g -O2 CFLAGS=" $CFLAGS" AC_PROG_INSTALL AC_PROG_MAKE_SET AC_PROG_RANLIB AC_PROG_CC AM_PROG_CC_C_O AM_PROG_AR ACX_ENABLE_DEBUG ACX_PTHREAD([LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" CC="$PTHREAD_CC"] AC_DEFINE(HAVE_PTHREAD, 1), AC_MSG_ERROR([no usable pthreads library found])) AC_CHECK_FUNCS([sem_timedwait]) AC_TYPE_MODE_T AC_TYPE_SIZE_T AC_HEADER_DIRENT AC_STRUCT_DIRENT_D_NAMLEN AC_HEADER_STDC AC_HEADER_STDBOOL AC_HEADER_TIME AC_STRUCT_TIMEZONE AC_CHECK_HEADERS([arpa/inet.h ctype.h errno.h fcntl.h limits.h \ netinet/in.h stdarg.h stdio.h stdlib.h string.h sys/param.h sys/time.h \ sys/types.h sys/socket.h sys/stat.h sys/un.h syslog.h sysexits.h unistd.h],[], AC_MSG_ERROR([unable to find required header files])) AC_CHECK_LIB(rt, sem_init, LIBS="$LIBS -lrt") AC_CHECK_POSIX_SEMAPHORES AC_CHECK_HEADERS([fts.h]) AC_CHECK_FUNCS([arc4random]) AC_REPLACE_FUNCS([daemon fts_open mkdtemp strlcpy]) AC_CHECK_FUNC([inet_ntop], [], [AC_SEARCH_LIBS(inet_ntop, [nsl])]) AC_CHECK_DIRFD AX_PATH_MILTER([8.12], [LIBS="$MILTER_LIBS $LIBS" LDFLAGS="$MILTER_LDFLAGS $LDFLAGS" CPPFLAGS="$CPPFLAGS $MILTER_CPPFLAGS"], AC_MSG_ERROR([required milter library and header not found])) AC_CHECK_FUNCS([smfi_insheader smfi_opensocket smfi_progress smfi_quarantine \ smfi_setbacklog]) AC_EILSEQ gl_EOVERFLOW AC_CHECK_AF_INET6 AC_CHECK_INET6_ADDRSTRLEN AC_CHECK_STRUCT_SOCKADDR_IN6 AC_CONFIG_FILES([Makefile amavisd-milter/Makefile compat/Makefile]) AC_CONFIG_FILES([autoconf.sh], [chmod +x autoconf.sh]) AC_LOCAL_STATE_DIR AC_WORKING_DIR AC_OUTPUT amavisd-milter-1.7.1/doc/000077500000000000000000000000001372525560500152155ustar00rootroot00000000000000amavisd-milter-1.7.1/doc/README.protocol000066400000000000000000000423431372525560500177430ustar00rootroot00000000000000AMAVIS POLICY DELEGATION PROTOCOL (AM.PDP) ========================================== Author: Mark Martinec Revisions: 2003-11-10 created 2005-03-18 2005-06-22 2006-08-18 (added attributes: version_server, insheader and quarantine), 2007-05-17 (allow attribute value: request=requeue) 2008-06-21 (new attribute partition_tag=xx on release requests) 2009-11-16 2010-01-22 (new attribute log_id on response) NOTE: at the end of this document there is a description (by Stephane Lentz) of the old protocol, now called AM.CL. Amavis policy delegation protocol (AM.PDP) is intended to replace the old amavis client protocol (AM.CL) as spoken between amavisd-new helper programs (amavis.c or amavis-milter.c) and the amavisd daemon. The server side is implemented by amavisd-new daemon. A sample AM.PDP client in Perl is helper-progs/amavis.pl, and a rewrite by Petr Rehor of the helper program amavis-milter.c to use the new AM.PDP protocol is available as a separate project, see: http://sourceforge.net/projects/amavisd-milter/ The new amavisd client/server protocol is based on the 'Postfix policy delegation protocol', described in the Postfix document file README_FILES/SMTPD_POLICY_README, which can be used to delegate policy decisions to an external server that runs outside Postfix. It is conceptually similar to sendmail/milter mechanism, but based on a documented text protocol which is easier to test and debug, instead of using an undocumented binary protocol such as sendmail/milter. There are a few departures from the original Postfix policy delegation protocol: - Postfix policy delegation protocol terminates lines with LF, AM.PDP protocol terminates lines with CR LF; ( This was changed to facilitate debugging via telnet, and to make it more similar to related protocols, e.g. SMTP, HTTP, FTP, ... ) - Postfix policy delegation protocol restricts the reply to one field only, AM.PDP allows arbitrary number of fields passed from server back to client; ( This allows additional functionality like modifying or removing recipient addresses, adding or editing mail header fields, etc. ) - Postfix policy delegation protocol expects the same attribute name to be used only once; AM.PDP allows repeated appearances of the attribute, which makes passing of lists easier (such as multiple recipient addresses); - attribute value may consist of more than one field. Fields are delimited by exactly one non-encoded space. Spaces within a field must be encoded like any other restricted character (see below). The protocol may be spoken over a Unix STREAM socket, or over an inet tcp socket. The client request is a sequence of name=value attributes, each terminated by CR LF. The sequence is terminated by an empty line (only CR LF). The server reply has the same structure. After client receives server response sequence terminated by an empty line, it may close the session, or issue another request on the same session. During normal operation the server should not close the socket until it has been closed by the client. Only under special circumstances is the server allowed to close the session, e.g. in response to a timeout or fatal error condition. The order of attributes does not matter, except for the 'request' attribute which must appear first in the client request. The policy client as well as the policy server should ignore any attributes that it does not care about. An attribute name must not contain non-encoded characters: "=", "%", space, null, CR or LF. An attribute value must not contain non-encoded characters: "%", space, null, CR, or LF. All restricted characters must be hex-encoded to a sequence of three characters: a percent sign, followed by two hex digits, e.g. %2A. Hexadecimal digits 0..9,a..f or 0..9,A..F are allowed. Any other character MAY be encoded as well. Although not mandated, it is prudent to encode all non-printable characters. The line length is not limited by this protocol. Both the server and the client must be prepared to handle arbitrary line lengths without breaking the protocol or rising security issues. Both are allowed to internally truncate unreasonably long lines to a sensible length, and issue a warning. Neither the client nor the server must make any assumptions that certain characters will not be used in the attribute name or values. E.g. a presence of encoded null or newline or other special character in the attribute name or value must be safely and appropriately handled. If such a character does not comply with the expected syntax, the case should be handled to the best of client or server understanding and capability, e.g. character ignored, attribute ignored, and/or a warning logged. The following example Perl expression may be used for encoding/decoding: to decode attribute name and attribute values: s/%([0-9a-fA-F]{2})/pack("C",hex($1))/eg to encode attribute name: s/([^0-9a-zA-Z_-])/sprintf("%%%02x",ord($1))/eg; to encode attribute values (each space-separated field individually): s/([^\041-\044\046-\176])/sprintf("%%%02x",ord($1))/eg; Attributes in the client request are: ------------------------------------- request=AM.PDP is a required first attribute, its value must be AM.PDP sender= specifies the envelope sender address (reverse-path). The attribute should appear exactly once. The attribute value syntax is specified in rfc5321 as 'Reverse-path' (i.e. smtp-quoted form, enclosed in <>); a null reverse path is specified as <>. recipient= specifies the envelope recipient address. The attribute appears once for each recipient address, the order of addresses must be preserved and might be significant for some setups or functions. The attribute value syntax is specified in rfc5321 as 'Forward-path'. tempdir=/var/amavis/amavis-milter-MWZmu9Di Specifies a temporary work directory to be used for mail unpacking, typically also containing the original mail file - see attribute 'mail_file' below. This attribute should be present exactly once. The server is allowed to use the specified directory to create additional temporary files if it chooses so. As a security precaution, currently amavisd restricts the temporary directory path, which must be a subdirectory under $TEMPBASE or $MYHOME. tempdir_removed_by=client Specifies the client will be responsible for removing the temporary directory. The server must not remove the file email.txt nor the directory, but it may remove temporary files and subdirectories it has created. tempdir_removed_by=server Specifies the server is responsible to remove the temporary directory if/when it deems appropriate. This is a default in the absence of this attribute (for compatibility with traditional amavis clients). mail_file=/var/amavis/amavis-milter-MWZmu9Di/email.txt Specifies a file name (full file path) of a file containing the original mail with header and body. This attribute should be present at most once. In its absence the file name defaults to /email.txt. delivery_care_of=client Specifies that server should NOT actively forward the mail to recipients, but should only report its opinion in its reply, and let the client act on it. This is a default in the absence of this attribute. delivery_care_of=server Specifies that server is responsible to actively forward the mail to recipients if it deems the message appropriate for forwarding. This attribute value indicates that the client has no capability or intention to forward mail by itself. queue_id=qid optional informational attribute: MTA queue id; protocol_name=ESMTP optional informational attribute: the name of the protocol used by the original SMTP client to deliver the mail to the client (or to its associated MTA). Common values are ESMTP, SMTP, LMTP. helo_name=b.example.com optional informational attribute: the value of the HELO or EHLO or LHLO command specified to our MTA by the original SMTP client; client_address=10.2.3.4 optional informational attribute: the IP address of the original SMTP client; client_name=mail.example.com optional informational attribute: the DNS name of the original SMTP client as obtained by DNS reverse mapping of the original SMTP client; policy_bank=TLS,ORIGINATING,MYNETS value is a comma-separated list of policy bank names. Names of nonexistent banks are silently ignored, so are leading and trailing spaces and TABs around each name. The order of policy bank loading generally follows the order in which information about a message were obtained: - interface or socket -based policy banks (when MTA connects to amavisd); - MYNETS (when client's IP address becomes known); - the list as specified in the policy_bank attribute of AM.PDP; - MYUSERS (when sender e-mail address becomes known); Attributes in the server response are: -------------------------------------- The current set of attributes maps almost exactly to the capabilities of libmilter, which should facilitate initial implementation. See sendmail libmilter documentation as well. version_server=2 Since amavisd-new-2.4.3 the 'version_server=2' is inserted, older versions did not provide this attribute (assumed version was 1). With version 2 the following changes to the protocol were made: - version_server=2 is provided by the server as the first attribute; - delheader and chgheader now stand in a response before insheader and addheader, assuming that milter MTA will execute these in the same order; - new attribute insheader, see below; - new attribute quarantine, see below. log_id=xxx Provided since amavisd-new-2.6.5, tells a log_id under which the amavisd daemon logged the request and related debugging events; delrcpt= The specified recipient should be removed from the list of recipients of this mail. The specified address must exactly match the recipient addresses as specified in the client request, i.e. a smtp-quoted form enclosed in <> should be specified; addrcpt= The specified recipient should be added to the list of recipients of this mail. Paired with 'delrcpt' the pair indicates an existing recipient address to be replaced by a modified address, e.g. to add an address extension. delheader=index hdr_head Specifies an existing mail header field should be removed. Index is a decimal integer indicating which of the header fields with the same head should be affected, count starts with 1. 'hdr_head' does not include a colon! chgheader=index hdr_head hdr_body Specifies an existing mail header field should have its body replaced by a new hdr_body. Index is a decimal integer indicating which of the header fields with the same head should be affected, count starts with 1. 'hdr_head' does not include a colon! insheader=index hdr_head hdr_body Similar to addheader, but specifies a mail header field to be inserted to the mail header at a given position, index 0 implies top of the header. Amavisd-new passes a value of $prepend_header_fields_hdridx as the index argument, which is configurable and defaults to 1 for compatibility with dkim-milter and dk-milter signing milters. Alternative useful value is 0, which may be used in absence of other milters inserting their header fields at index 1. Header fields to be prepended are listed in reverse order, the last one listed in AM.PDP is to appear at the top of a resulting mail header. Inserting a header field at an arbitrary position is a later addition to sendmail milter protocol (introduced with sendmail 8.13.0 2004-06-20) addheader=hdr_head hdr_body Specifies a mail header field to be appended to the mail header. Note the use of exactly two value fields, separated by exactly one space. As described above, spaces in each field must be hex-encoded. 'hdr_head' does not include the colon! replacebody=new_body Not implemented - for future consideration. quarantine=reason place message on hold or to a quarantine maintained by MTA, and supply a reason text (e.g. client may call smfi_quarantine milter routine; btw, smfi_quarantine was introduced with sendmail 8.13); For future use - it is currently (2.4.3 or earlier) never used. return_value=val where val can be any of: continue, accept, reject, discard, tempfail This attribute should be present exactly once, and indicates to the client what should be done with the mail and how the original SMTP session should be treated. setreply=rcode xcode message SMTP response code and text suggested by server to the client to be used in response to the original SMTP client; There are exactly three value fields, separated by a single space. exit_code=n Similar in semantics to return_value attribute, but uses sysexits.h -based exit values for the purpose. Useful with old amavis clients, but should be ignored by new clients, which should base its decision on return_value instead. An example ========== Indentation and text in parenthesis in the following example is not part of the actual session. $ telnet 127.0.0.1 9998 Trying 127.0.0.1... Connected to localhost.example.com. Escape character is '^]'. (client->server request) request=AM.PDP sender=me@example.com recipient=user1@example.net recipient=user2@example.net protocol_name=ESMTP client_address=10.2.3.4 tempdir=/var/amavis/amavis-milter-MWZmu9Di (server->client response) setreply=250 2.5.0 Ok,%20id=MWZmu9Di,%20continue%20delivery return_value=continue exit_code=0 (or:) setreply=250 2.7.1 Ok,%20discarded,%20UBE,%20id=mYOljdn2 return_value=discard exit_code=99 (or:) setreply=550 5.7.1 Message%20content%20rejected,%20UBE,%20id=S7uS4qvA return_value=reject exit_code=69 (or:) setreply=451 4.5.0 Error%20in%20processing,%20id=... return_value=tempfail exit_code=75 =============================================================================== Releasing a message from a quarantine: request=release (or request=requeue, available since 2.5.1) mail_id=xxxxxxxxxxxx secret_id=xxxxxxxxxxxx (authorizes a release) partition_tag=xx (optional, but recommended if info is available) quar_type=x F/Z/B/Q/M (file/zipfile/bsmtp/sql/mailbox) mail_file=... (optional: overrides automatics; $QUARANTINEDIR prepended) requested_by= (optional: lands in Resent-From:) sender= (optional: replaces envelope sender) recipient= (optional: replaces envelope recips) recipient= recipient=... In reply, for each recipient a SMTP status response is returned (similar to LMTP) quar_type defaults to Q if spam_quarantine_method is sql:, otherwise to F. =============================================================================== AMAVIS SIMPLE CLIENT/SERVER PROTOCOL (traditional) description by Stephane Lentz 2003-11-06 amavisd is the daemon part of AMAVIS in charge of scanning SMTP messages. It receives messages from other applications (clients) using either SMTP or a simple protocol which is detailed in this document. The protocol being used depends on the MTA and architecture chosen. The "simple protocol" is most often used with sendmail in a MILTER set-up (the client program in such a case is amavis-client). AMAVISD receives messages from clients through a UNIX socket : The UNIX socket used is by default /var/amavis/amavisd.sock . It is defined : - at the amavisd server level in amavisd.conf as $unix_socketname - at the client level when using MILTER (amavis-milter available in helper-progs) as a configure option : --with-sockname swith The protocol used between the client and server is simple (basic & limited). There is no possibility for the server to ask the client to remove/add/change headers. The server can only say if the message was detected as CLEAN, as UNSAFE and to be rejected/discarded) or not analysed successfully due to some errors. PROTOCOL IN DETAILS : The client connects to the AMAVISD server's socket. IF successful then for each incoming message : - the client computes and create a new temporary directory ($tempdir) to store the new incoming message. - the incoming message is stored as $tempdir/email.txt - the client sends the directory name to the SERVER - the server sends \1 to the client if the directory is ok - the client sends the envelope sender recipient address - the server sends \1 to the client if ok - the client sends the envelope recipient addresses one by one to the server: the client trims the address if it is longer than the maximum length possible the client sends this address to the server the server sends \1 to the client if ok - the client sends some request to analyze the message to the SERVER. The character used is EOT (end of transmission) :\3 - the server processes the mail stored in the directory ($tempdir/email.txt) - the server sends a STATUS number to the CLIENT. This number returned is either : EX_OK (2) : message CLEAN EX_UNAVAILABLE (69) : message UNSAFE to be rejected at the SMTP LEVEL (550 reject) 99 : message UNSAFE to be silently (250 code) discarded at the SMTP LEVEL EX_TEMPFAIL (75) : message not processed successfully (error in communication, or server error, ...)