diff options
author | Tony Finch <dot@dot.at> | 2005-05-23 16:58:55 +0000 |
---|---|---|
committer | Tony Finch <dot@dot.at> | 2005-05-23 16:58:55 +0000 |
commit | 870f6ba8a2945754a7f2f66097e3a64465fe1a04 (patch) | |
tree | 4660f28a3b0dfe5050966361e7873572efbc1884 /src | |
parent | 4df1e33e03e9edf6ee6cd328114e5eb102a85c9c (diff) |
Add the ratelimit ACL condition. This is mostly reasonably self-contained
except that it requires changes to most Makefiles in order to bring in the
maths library for the exp() function.
Diffstat (limited to 'src')
35 files changed, 432 insertions, 71 deletions
diff --git a/src/OS/Makefile-AIX b/src/OS/Makefile-AIX index 489ffe3b1..e758f7409 100644 --- a/src/OS/Makefile-AIX +++ b/src/OS/Makefile-AIX @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-AIX,v 1.2 2005/02/16 16:40:22 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-AIX,v 1.3 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for AIX # Written by Nick Waterman (nick@cimio.co.uk) @@ -25,6 +25,6 @@ CFLAGS = -mcpu=power4 -maix64 -O3 # Needed for vfork() and vfork() only? -LIBS = -lbsd +LIBS = -lbsd -lm # End diff --git a/src/OS/Makefile-BSDI b/src/OS/Makefile-BSDI index 10ea69895..2538c707e 100644 --- a/src/OS/Makefile-BSDI +++ b/src/OS/Makefile-BSDI @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-BSDI,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-BSDI,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for BSDI. Its antique link editor # cannot handle the TextPop overriding. @@ -13,7 +13,7 @@ XINCLUDE=-I$(X11)/include XLFLAGS=-L$(X11)/lib X11_LD_LIB=$(X11)/lib -LIBS_EXIMON=-lSM -lICE -lipc +LIBS_EXIMON=-lSM -lICE -lipc -lm EXIMON_TEXTPOP= EXIWHAT_PS_ARG=-ax diff --git a/src/OS/Makefile-CYGWIN b/src/OS/Makefile-CYGWIN index 52dd68456..8bf51216c 100644 --- a/src/OS/Makefile-CYGWIN +++ b/src/OS/Makefile-CYGWIN @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-CYGWIN,v 1.2 2004/11/10 10:36:48 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-CYGWIN,v 1.3 2005/05/23 16:58:55 fanf2 Exp $ # OS-specific file for Cygwin. @@ -6,7 +6,7 @@ HAVE_ICONV = yes CFLAGS= -g -Wall -O2 -LIBS= -lcrypt -lresolv +LIBS= -lcrypt -lresolv -lm LIBS_EXIM= -liconv EXIWHAT_PS_ARG=-as EXIWHAT_KILL_SIGNAL=-USR1 diff --git a/src/OS/Makefile-DGUX b/src/OS/Makefile-DGUX index 4f0439b93..11e5012f1 100644 --- a/src/OS/Makefile-DGUX +++ b/src/OS/Makefile-DGUX @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-DGUX,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-DGUX,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for DGUX # @@ -25,7 +25,7 @@ PERL_COMMAND=/usr/local/bin/perl CFLAGS=-O2 RANLIB=@true -LIBS=-lsocket -lnsl +LIBS=-lsocket -lnsl -lm LIBRESOLV=-lresolv DBMLIB=-ldbm diff --git a/src/OS/Makefile-FreeBSD b/src/OS/Makefile-FreeBSD index 179cbacb4..0527e8be4 100644 --- a/src/OS/Makefile-FreeBSD +++ b/src/OS/Makefile-FreeBSD @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-FreeBSD,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-FreeBSD,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for FreeBSD # There's no setting of CFLAGS here, to allow the system default @@ -11,7 +11,7 @@ CHOWN_COMMAND=/usr/sbin/chown HAVE_SA_LEN=YES # crypt() is in a separate library -LIBS=-lcrypt +LIBS=-lcrypt -lm # FreeBSD always ships with Berkeley DB USE_DB=yes diff --git a/src/OS/Makefile-GNU b/src/OS/Makefile-GNU index ef71d90d3..6c8f30eae 100644 --- a/src/OS/Makefile-GNU +++ b/src/OS/Makefile-GNU @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-GNU,v 1.3 2005/01/12 12:25:56 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-GNU,v 1.4 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for GNU and variants. @@ -13,7 +13,7 @@ CFLAGS ?= -O -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE DBMLIB = -ldb USE_DB = yes -LIBS = -lnsl -lcrypt +LIBS = -lnsl -lcrypt -lm LIBRESOLV = -lresolv X11=/usr/X11R6 diff --git a/src/OS/Makefile-GNUkFreeBSD b/src/OS/Makefile-GNUkFreeBSD index 03a8ac9e8..305640cca 100644 --- a/src/OS/Makefile-GNUkFreeBSD +++ b/src/OS/Makefile-GNUkFreeBSD @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-GNUkFreeBSD,v 1.1 2005/01/04 10:25:58 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-GNUkFreeBSD,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for GNU and variants. @@ -13,7 +13,7 @@ CFLAGS ?= -O -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE DBMLIB = -ldb USE_DB = yes -LIBS = -lnsl -lcrypt +LIBS = -lnsl -lcrypt -lm LIBRESOLV = -lresolv X11=/usr/X11R6 diff --git a/src/OS/Makefile-GNUkNetBSD b/src/OS/Makefile-GNUkNetBSD index 05f4f56aa..45cfc8b52 100644 --- a/src/OS/Makefile-GNUkNetBSD +++ b/src/OS/Makefile-GNUkNetBSD @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-GNUkNetBSD,v 1.1 2005/01/04 10:25:58 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-GNUkNetBSD,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for GNU and variants. @@ -13,7 +13,7 @@ CFLAGS ?= -O -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE DBMLIB = -ldb USE_DB = yes -LIBS = -lnsl -lcrypt +LIBS = -lnsl -lcrypt -lm LIBRESOLV = -lresolv X11=/usr/X11R6 diff --git a/src/OS/Makefile-IRIX b/src/OS/Makefile-IRIX index bab1e9910..6ffcf5eec 100644 --- a/src/OS/Makefile-IRIX +++ b/src/OS/Makefile-IRIX @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-IRIX,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-IRIX,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for IRIX @@ -6,7 +6,7 @@ HAVE_ICONV=yes BASENAME_COMMAND=/sbin/basename HOSTNAME_COMMAND=/usr/bsd/hostname CFLAGS=-OPT:Olimit=1500 -LIBS=-lmld +LIBS=-lmld -lm XINCLUDE=-I/usr/include/X11 vfork=fork RANLIB=@true diff --git a/src/OS/Makefile-IRIX6 b/src/OS/Makefile-IRIX6 index 3534e5d66..357edd030 100644 --- a/src/OS/Makefile-IRIX6 +++ b/src/OS/Makefile-IRIX6 @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-IRIX6,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-IRIX6,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for IRIX6 on 64-bit systems @@ -6,7 +6,7 @@ HAVE_ICONV=yes HOSTNAME_COMMAND=/usr/bsd/hostname CFLAGS=-O2 -n32 -OPT:Olimit=4000 LFLAGS=-n32 -LIBS=-lelf +LIBS=-lelf -lm XINCLUDE=-I/usr/include/X11 XLFLAGS= vfork=fork diff --git a/src/OS/Makefile-IRIX632 b/src/OS/Makefile-IRIX632 index 5b09364df..757c4b8a5 100644 --- a/src/OS/Makefile-IRIX632 +++ b/src/OS/Makefile-IRIX632 @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-IRIX632,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-IRIX632,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for IRIX 6 on 32-bit systems. # There seems to be some variation. The commented settings show @@ -10,7 +10,7 @@ HOSTNAME_COMMAND=/usr/bsd/hostname CFLAGS=-32 LFLAGS=-32 #LIBS=-lmld -LIBS=-lelf +LIBS=-lelf -lm XINCLUDE=-I/usr/include/X11 vfork=fork RANLIB=@true diff --git a/src/OS/Makefile-IRIX65 b/src/OS/Makefile-IRIX65 index b678e37ae..27476c5e2 100644 --- a/src/OS/Makefile-IRIX65 +++ b/src/OS/Makefile-IRIX65 @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-IRIX65,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-IRIX65,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for IRIX 6.5 @@ -10,7 +10,7 @@ CFLAGS=-O2 -OPT:Olimit=0 LFLAGS=-Wl,-LD_MSG:off=85 LFLAGS= # nlist has moved from libmld to libelf -LIBS=-lelf +LIBS=-lelf -lm XINCLUDE=-I/usr/include/X11 vfork=fork RANLIB=@true diff --git a/src/OS/Makefile-Linux b/src/OS/Makefile-Linux index d933c056a..94290f2b9 100644 --- a/src/OS/Makefile-Linux +++ b/src/OS/Makefile-Linux @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-Linux,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-Linux,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for Linux. This is for modern Linuxes, # which use libc6. @@ -14,7 +14,7 @@ CFLAGS=-O -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE DBMLIB = -ldb USE_DB = yes -LIBS = -lnsl -lcrypt +LIBS = -lnsl -lcrypt -lm LIBRESOLV = -lresolv X11=/usr/X11R6 diff --git a/src/OS/Makefile-NetBSD b/src/OS/Makefile-NetBSD index 0f00789cb..1e7cbc127 100644 --- a/src/OS/Makefile-NetBSD +++ b/src/OS/Makefile-NetBSD @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-NetBSD,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-NetBSD,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for NetBSD (ELF object format) @@ -7,7 +7,7 @@ CFLAGS=-O HAVE_SA_LEN=YES HAVE_IPV6=YES -LIBS=-lcrypt +LIBS=-lcrypt -lm X11=/usr/X11R6 XINCLUDE=-I$(X11)/include diff --git a/src/OS/Makefile-NetBSD-a.out b/src/OS/Makefile-NetBSD-a.out index 1ef7a86c8..350689d2e 100644 --- a/src/OS/Makefile-NetBSD-a.out +++ b/src/OS/Makefile-NetBSD-a.out @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-NetBSD-a.out,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-NetBSD-a.out,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for NetBSD (a.out/COFF object format) @@ -7,7 +7,7 @@ CFLAGS=-O HAVE_SA_LEN=YES HAVE_IPV6=YES -LIBS=-lcrypt +LIBS=-lcrypt -lm X11=/usr/X11R6 XINCLUDE=-I$(X11)/include diff --git a/src/OS/Makefile-OSF1 b/src/OS/Makefile-OSF1 index b5a61fb19..b513db88e 100644 --- a/src/OS/Makefile-OSF1 +++ b/src/OS/Makefile-OSF1 @@ -1,9 +1,9 @@ -# $Cambridge: exim/src/OS/Makefile-OSF1,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-OSF1,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for OSF1 CFLAGS=-O -LIBS=-liconv +LIBS=-liconv -lm HAVE_CRYPT16=yes HAVE_ICONV=yes HOSTNAME_COMMAND=/usr/bin/hostname diff --git a/src/OS/Makefile-OpenUNIX b/src/OS/Makefile-OpenUNIX index 2d543b64f..c0dbe420c 100644 --- a/src/OS/Makefile-OpenUNIX +++ b/src/OS/Makefile-OpenUNIX @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-OpenUNIX,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-OpenUNIX,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for OpenUNIX @@ -6,7 +6,7 @@ CC=/usr/bin/cc CFLAGS=-O -I/usr/local/include LFLAGS=-L/usr/local/lib -LIBS=-lsocket -lnsl -lelf -lgen -lresolv +LIBS=-lsocket -lnsl -lelf -lgen -lresolv -lm EXTRALIBS_EXIMON=-lICE -lSM RANLIB=@true diff --git a/src/OS/Makefile-QNX b/src/OS/Makefile-QNX index d0f74608d..8c7375f9a 100644 --- a/src/OS/Makefile-QNX +++ b/src/OS/Makefile-QNX @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-QNX,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-QNX,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific makefile for QNX @@ -21,7 +21,7 @@ LIBIDENTCFLAGS= RANLIB=@true DBMLIB=-ldb USE_DB=yes -LIBS=-lsocket +LIBS=-lsocket -lm X11=/usr/X11R6 XINCLUDE=-I$(X11)/include diff --git a/src/OS/Makefile-SCO b/src/OS/Makefile-SCO index 4366f387b..d328b57cd 100644 --- a/src/OS/Makefile-SCO +++ b/src/OS/Makefile-SCO @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-SCO,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-SCO,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for SCO @@ -10,7 +10,7 @@ CFLAGS=-b elf RANLIB=@true DBMLIB=-lndbm ERRNO_QUOTA=0 -LIBS=-lsocket +LIBS=-lsocket -lm HAVE_ICONV=yes X11=/usr/lib/X11 diff --git a/src/OS/Makefile-SCO_SV b/src/OS/Makefile-SCO_SV index 48b5f2278..6d65862a3 100644 --- a/src/OS/Makefile-SCO_SV +++ b/src/OS/Makefile-SCO_SV @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-SCO_SV,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-SCO_SV,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for SCO_SV release 5 (tested on 5.0.5 & 5.0.5) # (see the UNIX_SV files for SCO 4.2) @@ -11,7 +11,7 @@ CFLAGS=-melf -O3 -m486 LFLAGS=-L/lib -L/usr/lib -L/usr/local/lib -LIBS=-ltinfo -lm -lsocket +LIBS=-ltinfo -lsocket -lm HAVE_ICONV=yes diff --git a/src/OS/Makefile-SunOS5 b/src/OS/Makefile-SunOS5 index 1afef4d23..ebe436779 100644 --- a/src/OS/Makefile-SunOS5 +++ b/src/OS/Makefile-SunOS5 @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-SunOS5,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-SunOS5,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for SunOS5 @@ -10,7 +10,7 @@ BASENAME_COMMAND=look_for_it HOSTNAME_COMMAND=look_for_it RANLIB=@true -LIBS=-lsocket -lnsl -lkstat +LIBS=-lsocket -lnsl -lkstat -lm LIBRESOLV=-lresolv EXIWHAT_MULTIKILL_CMD=pkill diff --git a/src/OS/Makefile-SunOS5-hal b/src/OS/Makefile-SunOS5-hal index 014bac1e1..8b4bcdfbf 100644 --- a/src/OS/Makefile-SunOS5-hal +++ b/src/OS/Makefile-SunOS5-hal @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-SunOS5-hal,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-SunOS5-hal,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for SunOS5 on a HAL @@ -11,7 +11,7 @@ CFLAGS=-O -KV7 LIBIDENTCFLAGS="-KV7 -O -DHAVE_ANSIHEADERS" LIBIDENTNAME=sunos5 RANLIB=@true -LIBS=-lsocket -lnsl -lkstat +LIBS=-lsocket -lnsl -lkstat -lm LIBRESOLV=-lresolv X11=/usr/X11R6 XINCLUDE=-I$(X11)/include diff --git a/src/OS/Makefile-UNIX_SV b/src/OS/Makefile-UNIX_SV index 1f56c59fa..79660ed31 100644 --- a/src/OS/Makefile-UNIX_SV +++ b/src/OS/Makefile-UNIX_SV @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-UNIX_SV,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-UNIX_SV,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for SCO SVR4.2MP (and maybe Unixware) # @@ -17,7 +17,7 @@ CFLAGS=-O RANLIB=@true DBMLIB=-lgdbm -L/usr/local/lib ERRNO_QUOTA=0 -LIBS=-lsocket -lelf -lgen -lnsl -lresolv +LIBS=-lsocket -lelf -lgen -lnsl -lresolv -lm X11=/usr/lib/X11 XINCLUDE=-I/usr/include/X11 diff --git a/src/OS/Makefile-USG b/src/OS/Makefile-USG index b49feecc9..421e9b94c 100644 --- a/src/OS/Makefile-USG +++ b/src/OS/Makefile-USG @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-USG,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-USG,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for Unixware 2.x # @@ -25,7 +25,7 @@ RANLIB=@true DBMLIB=-ldb -L/usr/local/lib USE_DB=YES ERRNO_QUOTA=0 -LIBS=-lsocket -lelf -lgen -lnsl -lresolv +LIBS=-lsocket -lelf -lgen -lnsl -lresolv -lm X11=/usr/lib/X11 XINCLUDE=-I/usr/include/X11 diff --git a/src/OS/Makefile-Unixware7 b/src/OS/Makefile-Unixware7 index 5c228cc7f..6c7fab46b 100644 --- a/src/OS/Makefile-Unixware7 +++ b/src/OS/Makefile-Unixware7 @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-Unixware7,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-Unixware7,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for Unixware7 # Based on information from James FitzGibbon <james@ehlo.com> @@ -17,7 +17,7 @@ LFLAGS=-L/usr/local/lib HAVE_ICONV=yes -LIBS=-lsocket -lnsl -lelf -lgen -lresolv +LIBS=-lsocket -lnsl -lelf -lgen -lresolv -lm # Removed on the advice of Larry Rosenman # EXTRALIBS=-lwrap diff --git a/src/OS/Makefile-mips b/src/OS/Makefile-mips index 9df94e2d7..9cee5174b 100644 --- a/src/OS/Makefile-mips +++ b/src/OS/Makefile-mips @@ -1,4 +1,4 @@ -# $Cambridge: exim/src/OS/Makefile-mips,v 1.1 2004/10/06 15:07:39 ph10 Exp $ +# $Cambridge: exim/src/OS/Makefile-mips,v 1.2 2005/05/23 16:58:55 fanf2 Exp $ # Exim: OS-specific make file for RiscOS4bsd @@ -6,7 +6,7 @@ HOSTNAME_COMMAND=/usr/ucb/hostname EXIT_FAILURE=1 EXIT_SUCCESS=0 LIBRESOLV=-lresolv -LIBS=-liberty +LIBS=-liberty -lm XINCLUDE=-I/usr/X11R6/include CFLAGS=-O diff --git a/src/src/acl.c b/src/src/acl.c index 5ea853521..8125e38c9 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/acl.c,v 1.33 2005/05/23 15:28:38 fanf2 Exp $ */ +/* $Cambridge: exim/src/src/acl.c,v 1.34 2005/05/23 16:58:56 fanf2 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -63,6 +63,7 @@ ACLC_CONDITION, ACLC_CONTROL, #ifdef WITH_CONTENT_SCAN ACLC_MIME_REGEX, #endif + ACLC_RATELIMIT, ACLC_RECIPIENTS, #ifdef WITH_CONTENT_SCAN ACLC_REGEX, @@ -110,6 +111,7 @@ static uschar *conditions[] = { US"acl", US"authenticated", #ifdef WITH_CONTENT_SCAN US"mime_regex", #endif + US"ratelimit", US"recipients", #ifdef WITH_CONTENT_SCAN US"regex", @@ -171,6 +173,7 @@ static uschar cond_expand_at_top[] = { #ifdef WITH_CONTENT_SCAN TRUE, /* mime_regex */ #endif + TRUE, /* ratelimit */ FALSE, /* recipients */ #ifdef WITH_CONTENT_SCAN TRUE, /* regex */ @@ -227,6 +230,7 @@ static uschar cond_modifiers[] = { #ifdef WITH_CONTENT_SCAN FALSE, /* mime_regex */ #endif + FALSE, /* ratelimit */ FALSE, /* recipients */ #ifdef WITH_CONTENT_SCAN FALSE, /* regex */ @@ -363,6 +367,8 @@ static unsigned int cond_forbids[] = { ~(1<<ACL_WHERE_MIME), /* mime_regex */ #endif + 0, /* ratelimit */ + (unsigned int) ~(1<<ACL_WHERE_RCPT), /* recipients */ @@ -1884,6 +1890,283 @@ return d->value; /************************************************* +* Handle rate limiting * +*************************************************/ + +/* Called by acl_check_condition() below to calculate the result +of the ACL ratelimit condition. + +Note that the return value might be slightly unexpected: if the +sender's rate is above the limit then the result is OK. This is +similar to the dnslists condition, and is so that you can write +ACL clauses like: defer ratelimit = 15 / 1h + +Arguments: + arg the option string for ratelimit= + log_msgptr for error messages + +Returns: OK - Sender's rate is above limit + FAIL - Sender's rate is below limit + DEFER - Problem opening ratelimit database + ERROR - Syntax error in options. +*/ + +static int +acl_ratelimit(uschar *arg, uschar **log_msgptr) +{ +double limit, period; +uschar *ss, *key = arg; +int sep = '/'; +BOOL have_key = FALSE, leaky = FALSE, strict = FALSE; +BOOL per_byte = FALSE, per_cmd = FALSE, per_conn = FALSE, per_mail = FALSE; +int old_pool, rc; +tree_node **anchor, *t; +open_db dbblock, *dbm; +dbdata_ratelimit *dbd; +struct timeval tv; + +/* Parse the first two options and record their values in expansion +variables. These variables allow the configuration to have informative +error messages based on rate limits obtained from a table lookup. */ + +/* First is the maximum number of messages per period and maximum burst +size, which must be greater than or equal to zero. Zero is useful for +rate measurement as opposed to rate limiting. */ + +sender_rate_limit = string_nextinlist(&arg, &sep, NULL, 0); +if (sender_rate_limit == NULL) + limit = -1.0; +else + { + limit = Ustrtod(sender_rate_limit, &ss); + if (tolower(*ss) == 'k') { limit *= 1024.0; ss++; } + else if (tolower(*ss) == 'm') { limit *= 1024.0*1024.0; ss++; } + else if (tolower(*ss) == 'g') { limit *= 1024.0*1024.0*1024.0; ss++; } + } +if (limit < 0.0 || *ss != 0) + { + *log_msgptr = string_sprintf("syntax error in argument for " + "\"ratelimit\" condition: \"%s\" is not a positive number", + sender_rate_limit); + return ERROR; + } + +/* Second is the rate measurement period and exponential smoothing time +constant. This must be strictly greater than zero, because zero leads to +run-time division errors. */ + +sender_rate_period = string_nextinlist(&arg, &sep, NULL, 0); +if (sender_rate_period == NULL) period = -1.0; +else period = readconf_readtime(sender_rate_period, 0, FALSE); +if (period <= 0.0) + { + *log_msgptr = string_sprintf("syntax error in argument for " + "\"ratelimit\" condition: \"%s\" is not a time value", + sender_rate_period); + return ERROR; + } + +/* Parse the other options. Should we check if the per_* options are being +used in ACLs where they don't make sense, e.g. per_mail in the connect ACL? */ + +while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size)) + != NULL) + { + if (strcmpic(ss, US"leaky") == 0) leaky = TRUE; + else if (strcmpic(ss, US"strict") == 0) strict = TRUE; + else if (strcmpic(ss, US"per_byte") == 0) per_byte = TRUE; + else if (strcmpic(ss, US"per_cmd") == 0) per_cmd = TRUE; + else if (strcmpic(ss, US"per_conn") == 0) per_conn = TRUE; + else if (strcmpic(ss, US"per_mail") == 0) per_mail = TRUE; + else if (strcmpic(ss, US"per_rcpt") == 0) per_cmd = TRUE; /* alias */ + else have_key = TRUE; + } +if (leaky + strict > 1 || per_byte + per_cmd + per_conn + per_mail > 1) + { + *log_msgptr = US"conflicting options for \"ratelimit\" condition"; + return ERROR; + } + +/* Default option values */ +if (!strict) leaky = TRUE; +if (!per_byte && !per_cmd && !per_conn) per_mail = TRUE; + +/* We use the whole of the argument list as the lookup key, because it doesn't +make sense to use the same stored data if any of the arguments are different. +If there is no explicit key, use the sender_host_address. If there is no +sender_host_address (e.g. -bs or acl_not_smtp) then we simply omit it. */ + +if (!have_key && sender_host_address != NULL) + key = string_sprintf("%s / %s", key, sender_host_address); + +HDEBUG(D_acl) debug_printf("ratelimit condition limit=%.0f period=%.0f key=%s\n", + limit, period, key); + +/* If we are dealing with rate limits per connection, per message, or per byte, +see if we have already computed the rate by looking in the relevant tree. For +per-connection rate limiting, store tree nodes and dbdata in the permanent pool +so that they survive across resets. */ + +anchor = NULL; +old_pool = store_pool; + +if (per_conn) + { + anchor = &ratelimiters_conn; + store_pool = POOL_PERM; + } +if (per_mail || per_byte) + anchor = &ratelimiters_mail; + +if (anchor != NULL && (t = tree_search(*anchor, key)) != NULL) + { + dbd = t->data.ptr; + /* The following few lines duplicate some of the code below. */ + if (dbd->rate > limit) rc = OK; + else rc = FAIL; + store_pool = old_pool; + sender_rate = string_sprintf("%.1f", dbd->rate); + HDEBUG(D_acl) + debug_printf("ratelimit found pre-computed rate %s\n", sender_rate); + return rc; + } + +/* We aren't using a pre-computed rate, so get a previously recorded +rate from the database, update it, and write it back. If there's no +previous rate for this key, create one. */ + +dbm = dbfn_open(US"ratelimit", O_RDWR, &dbblock, TRUE); +if (dbm == NULL) + { + store_pool = old_pool; + sender_rate = NULL; + HDEBUG(D_acl) debug_printf("ratelimit database not available\n"); + *log_msgptr = US"ratelimit database not available"; + return DEFER; + } +dbd = dbfn_read(dbm, key); + +gettimeofday(&tv, NULL); + +if (dbd == NULL) + { + HDEBUG(D_acl) debug_printf("ratelimit initializing new key's data\n"); + dbd = store_get(sizeof(dbdata_ratelimit)); + dbd->time_stamp = tv.tv_sec; + dbd->time_usec = tv.tv_usec; + dbd->rate = 0.0; + } +else + { + /* The smoothed rate is computed using an exponentially weighted moving + average adjusted for variable sampling intervals. The standard EWMA for + a fixed sampling interval is: f'(t) = (1 - a) * f(t) + a * f'(t - 1) + where f() is the measured value and f'() is the smoothed value. + + Old data decays out of the smoothed value exponentially, such that data n + samples old is multiplied by a^n. The exponential decay time constant p + is defined such that data p samples old is multiplied by 1/e, which means + that a = exp(-1/p). We can maintain the same time constant for a variable + sampling interval i by using a = exp(-i/p). + + The rate we are measuring is messages per period, suitable for directly + comparing with the limit. The average rate between now and the previous + message is period / interval, which we feed into the EWMA as the sample. + + It turns out that the number of messages required for the smoothed rate + to reach the limit when they are sent in a burst is equal to the limit. + This can be seen by analysing the value of the smoothed rate after N + messages sent at even intervals. Let k = (1 - a) * p/i + + rate_1 = (1 - a) * p/i + a * rate_0 + = k + a * rate_0 + rate_2 = k + a * rate_1 + = k + a * k + a^2 * rate_0 + rate_3 = k + a * k + a^2 * k + a^3 * rate_0 + rate_N = rate_0 * a^N + k * SUM(x=0..N-1)(a^x) + = rate_0 * a^N + k * (1 - a^N) / (1 - a) + = rate_0 * a^N + p/i * (1 - a^N) + + When N is large, a^N -> 0 so rate_N -> p/i as desired. + + rate_N = p/i + (rate_0 - p/i) * a^N + a^N = (rate_N - p/i) / (rate_0 - p/i) + N * -i/p = log((rate_N - p/i) / (rate_0 - p/i)) + N = p/i * log((rate_0 - p/i) / (rate_N - p/i)) + + Numerical analysis of the above equation, setting the computed rate to + increase from rate_0 = 0 to rate_N = limit, shows that for large sending + rates, p/i, the number of messages N = limit. So limit serves as both the + maximum rate measured in messages per period, and the maximum number of + messages that can be sent in a fast burst. */ + + double this_time = (double)tv.tv_sec + + (double)tv.tv_usec / 1000000.0; + double prev_time = (double)dbd->time_stamp + + (double)dbd->time_usec / 1000000.0; + double interval = this_time - prev_time; + + double i_over_p = interval / period; + double a = exp(-i_over_p); + + /* We must avoid division by zero, and deal gracefully with the clock going + backwards. If we blunder ahead when time is in reverse then the computed + rate will become bogusly huge. Clamp i/p to a very small number instead. */ + + if (i_over_p <= 0.0) i_over_p = 1e-9; + + dbd->time_stamp = tv.tv_sec; + dbd->time_usec = tv.tv_usec; + + /* If we are measuring the rate in bytes per period, multiply the + measured rate by the message size. If we don't know the message size + then it's safe to just use a value of zero and let the recorded rate + decay as if nothing happened. */ + + if (per_byte) + dbd->rate = (message_size < 0 ? 0.0 : (double)message_size) + * (1 - a) / i_over_p + a * dbd->rate; + else + dbd->rate = (1 - a) / i_over_p + a * dbd->rate; + } + +if (dbd->rate > limit) rc = OK; + else rc = FAIL; + +/* Update the state if the rate is low or if we are being strict. If we +are in leaky mode and the sender's rate is too high, we do not update +the recorded rate in order to avoid an over-aggressive sender's retry +rate preventing them from getting any email through. */ + +if (rc == FAIL || !leaky) + dbfn_write(dbm, key, dbd, sizeof(dbdata_ratelimit)); +dbfn_close(dbm); + +/* Store the result in the tree for future reference, if necessary. */ + +if (anchor != NULL) + { + t = store_get(sizeof(tree_node) + Ustrlen(key)); + t->data.ptr = dbd; + Ustrcpy(t->name, key); + (void)tree_insertnode(anchor, t); + } + +/* We create the formatted version of the sender's rate very late in +order to ensure that it is done using the correct storage pool. */ + +store_pool = old_pool; +sender_rate = string_sprintf("%.1f", dbd->rate); + +HDEBUG(D_acl) + debug_printf("ratelimit computed rate %s\n", sender_rate); + +return rc; +} + + + +/************************************************* * Handle conditions/modifiers on an ACL item * *************************************************/ @@ -2415,6 +2698,10 @@ for (; cb != NULL; cb = cb->next) break; #endif + case ACLC_RATELIMIT: + rc = acl_ratelimit(arg, log_msgptr); + break; + case ACLC_RECIPIENTS: rc = match_address_list(addr->address, TRUE, TRUE, &arg, NULL, -1, 0, &recipient_data); diff --git a/src/src/dbstuff.h b/src/src/dbstuff.h index bb429126e..82f7ecc2a 100644 --- a/src/src/dbstuff.h +++ b/src/src/dbstuff.h @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/dbstuff.h,v 1.2 2005/01/04 10:00:42 ph10 Exp $ */ +/* $Cambridge: exim/src/src/dbstuff.h,v 1.3 2005/05/23 16:58:56 fanf2 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -363,8 +363,8 @@ after reading data. */ /* Basic DB type */ typedef struct { - GDBM_FILE gdbm; /* Database */ - datum lkey; /* Last key, for scans */ + GDBM_FILE gdbm; /* Database */ + datum lkey; /* Last key, for scans */ } EXIM_DB; /* Cursor type, not used with gdbm: just set up a dummy */ @@ -631,4 +631,15 @@ typedef struct { } dbdata_serialize; +/* This structure records the information required for the ratelimit +ACL condition. */ + +typedef struct { + time_t time_stamp; + /*************/ + int time_usec; /* Fractional part of time, from gettimeofday() */ + double rate; /* Smoothed sending rate at that time */ +} dbdata_ratelimit; + + /* End of dbstuff.h */ diff --git a/src/src/exim.h b/src/src/exim.h index bfe4819a2..44e4ab31d 100644 --- a/src/src/exim.h +++ b/src/src/exim.h @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/exim.h,v 1.13 2005/05/10 22:39:20 tom Exp $ */ +/* $Cambridge: exim/src/src/exim.h,v 1.14 2005/05/23 16:58:56 fanf2 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -49,6 +49,7 @@ making unique names. */ #include <ctype.h> #include <locale.h> +#include <math.h> #include <signal.h> #include <stdarg.h> #include <stddef.h> diff --git a/src/src/exim_dbutil.c b/src/src/exim_dbutil.c index 09783a959..a8dbe61b8 100644 --- a/src/src/exim_dbutil.c +++ b/src/src/exim_dbutil.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/exim_dbutil.c,v 1.3 2005/01/04 10:00:42 ph10 Exp $ */ +/* $Cambridge: exim/src/src/exim_dbutil.c,v 1.4 2005/05/23 16:58:56 fanf2 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -63,10 +63,11 @@ not too much extra baggage. */ /* Identifiers for the different database types. */ -#define type_retry 1 -#define type_wait 2 -#define type_misc 3 -#define type_callout 4 +#define type_retry 1 +#define type_wait 2 +#define type_misc 3 +#define type_callout 4 +#define type_ratelimit 5 @@ -113,7 +114,7 @@ static void usage(uschar *name, uschar *options) { printf("Usage: exim_%s%s <spool-directory> <database-name>\n", name, options); -printf(" <database-name> = retry | misc | wait-<transport-name> | callout\n"); +printf(" <database-name> = retry | misc | wait-<transport-name> | callout | ratelimit\n"); exit(1); } @@ -135,6 +136,7 @@ if (argc == 3) if (Ustrcmp(argv[2], "misc") == 0) return type_misc; if (Ustrncmp(argv[2], "wait-", 5) == 0) return type_wait; if (Ustrcmp(argv[2], "callout") == 0) return type_callout; + if (Ustrcmp(argv[2], "ratelimit") == 0) return type_ratelimit; } usage(name, options); return -1; /* Never obeyed */ @@ -536,6 +538,7 @@ while (key != NULL) dbdata_retry *retry; dbdata_wait *wait; dbdata_callout_cache *callout; + dbdata_ratelimit *ratelimit; int count_bad = 0; int i, length; uschar *t; @@ -662,6 +665,15 @@ while (key != NULL) } break; + + case type_ratelimit: + ratelimit = (dbdata_ratelimit *)value; + + printf("%s.%06d rate: %10.3f key: %s\n", + print_time(ratelimit->time_stamp), ratelimit->time_usec, + ratelimit->rate, keybuffer); + + break; } store_reset(value); } @@ -733,6 +745,7 @@ for(;;) dbdata_retry *retry; dbdata_wait *wait; dbdata_callout_cache *callout; + dbdata_ratelimit *ratelimit; int i, oldlength; uschar *t; uschar field[256], value[256]; @@ -873,6 +886,29 @@ for(;;) break; } break; + + case type_ratelimit: + ratelimit = (dbdata_ratelimit *)value; + switch(fieldno) + { + case 0: + if ((tt = read_time(value)) > 0) ratelimit->time_stamp = tt; + else printf("bad time value\n"); + break; + + case 1: + ratelimit->time_usec = Uatoi(value); + + case 2: + ratelimit->rate = Ustrtod(value, NULL); + break; + + default: + printf("unknown field number\n"); + verify = 0; + break; + } + break; } dbfn_write(dbm, name, record, length); @@ -970,6 +1006,13 @@ for(;;) callout->random_result); } break; + + case type_ratelimit: + ratelimit = (dbdata_ratelimit *)value; + printf("0 time stamp: %s\n", print_time(ratelimit->time_stamp)); + printf("1 fract. time: .%06d\n", ratelimit->time_usec); + printf("2 sender rate: % .3f\n", ratelimit->rate); + break; } } diff --git a/src/src/expand.c b/src/src/expand.c index 8677ccb5b..b0c1d5340 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/expand.c,v 1.21 2005/05/10 10:19:11 ph10 Exp $ */ +/* $Cambridge: exim/src/src/expand.c,v 1.22 2005/05/23 16:58:56 fanf2 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -466,6 +466,9 @@ static var_entry var_table[] = { { "sender_host_name", vtype_host_lookup, NULL }, { "sender_host_port", vtype_int, &sender_host_port }, { "sender_ident", vtype_stringptr, &sender_ident }, + { "sender_rate", vtype_stringptr, &sender_rate }, + { "sender_rate_limit", vtype_stringptr, &sender_rate_limit }, + { "sender_rate_period", vtype_stringptr, &sender_rate_period }, { "sender_rcvhost", vtype_stringptr, &sender_rcvhost }, { "sender_verify_failure",vtype_stringptr, &sender_verify_failure }, { "smtp_active_hostname", vtype_stringptr, &smtp_active_hostname }, diff --git a/src/src/globals.c b/src/src/globals.c index 8928e451f..9ad36daf1 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/globals.c,v 1.25 2005/05/23 15:28:38 fanf2 Exp $ */ +/* $Cambridge: exim/src/src/globals.c,v 1.26 2005/05/23 16:58:56 fanf2 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -777,6 +777,8 @@ BOOL queue_smtp = FALSE; uschar *queue_smtp_domains = NULL; unsigned int random_seed = 0; +tree_node *ratelimiters_conn = NULL; +tree_node *ratelimiters_mail = NULL; uschar *raw_active_hostname = NULL; uschar *raw_sender = NULL; uschar **raw_recipients = NULL; @@ -964,6 +966,9 @@ BOOL sender_host_notsocket = FALSE; BOOL sender_host_unknown = FALSE; uschar *sender_ident = NULL; BOOL sender_local = FALSE; +uschar *sender_rate = NULL; +uschar *sender_rate_limit = NULL; +uschar *sender_rate_period = NULL; uschar *sender_rcvhost = NULL; BOOL sender_set_untrusted = FALSE; uschar *sender_unqualified_hosts = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index 40785173d..58538975d 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/globals.h,v 1.17 2005/05/23 15:28:38 fanf2 Exp $ */ +/* $Cambridge: exim/src/src/globals.h,v 1.18 2005/05/23 16:58:56 fanf2 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -505,6 +505,8 @@ extern BOOL queue_smtp; /* Disable all immediate STMP (-odqs)*/ extern uschar *queue_smtp_domains; /* Ditto, for these domains */ extern unsigned int random_seed; /* Seed for random numbers */ +extern tree_node *ratelimiters_conn; /* Results of connection ratelimit checks */ +extern tree_node *ratelimiters_mail; /* Results of per-mail ratelimit checks */ extern uschar *raw_active_hostname; /* Pre-expansion */ extern uschar *raw_sender; /* Before rewriting */ extern uschar **raw_recipients; /* Before rewriting */ @@ -579,6 +581,9 @@ extern BOOL sender_host_notsocket; /* Set for -bs and -bS */ extern BOOL sender_host_unknown; /* TRUE for -bs and -bS except inetd */ extern uschar *sender_ident; /* Sender identity via RFC 1413 */ extern BOOL sender_local; /* TRUE for local senders */ +extern uschar *sender_rate; /* Sender rate computed by ACL */ +extern uschar *sender_rate_limit; /* Configured rate limit */ +extern uschar *sender_rate_period; /* Configured smoothing period */ extern uschar *sender_rcvhost; /* Host data for Received: */ extern BOOL sender_set_untrusted; /* Sender set by untrusted caller */ extern uschar *sender_unqualified_hosts; /* Permitted unqualified senders */ diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index dfed7b8f5..02264d280 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/smtp_in.c,v 1.18 2005/05/23 15:28:38 fanf2 Exp $ */ +/* $Cambridge: exim/src/src/smtp_in.c,v 1.19 2005/05/23 16:58:56 fanf2 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -838,6 +838,10 @@ spf_smtp_comment = NULL; #endif body_linecount = body_zerocount = 0; +sender_rate = sender_rate_limit = sender_rate_period = NULL; +ratelimiters_mail = NULL; /* Updated by ratelimit ACL condition */ + /* Note that ratelimiters_conn persists across resets. */ + for (i = 0; i < ACL_M_MAX; i++) acl_var[ACL_C_MAX + i] = NULL; /* The message body variables use malloc store. They may be set if this is diff --git a/src/src/string.c b/src/src/string.c index 2f69cd494..9edcee567 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/string.c,v 1.2 2005/01/04 10:00:42 ph10 Exp $ */ +/* $Cambridge: exim/src/src/string.c,v 1.3 2005/05/23 16:58:56 fanf2 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -1070,9 +1070,11 @@ while (*fp != 0) break; /* %f format is inherently insecure if the numbers that it may be - handed are unknown (e.g. 1e300). However, in Exim, the only use of %f - is for printing load averages, and these are actually stored as integers - (load average * 1000) so the size of the numbers is constrained. */ + handed are unknown (e.g. 1e300). However, in Exim, %f is used for + printing load averages, and these are actually stored as integers + (load average * 1000) so the size of the numbers is constrained. + It is also used for formatting sending rates, where the simplicity + of the format prevents overflow. */ case 'f': case 'e': |