summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org.ua>2007-05-01 07:59:58 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2007-05-01 07:59:58 (GMT)
commit732036d9d146940806635402ce7a208fa847e12f (patch) (side-by-side diff)
treeddf53ac4e9d65429e86479f6d918983dda74aac1
parent4dd5e7bb0306f5225b8e307dacde98807d3676bb (diff)
downloadmailfromd-alpha_3_1_91_berkeley_txn.tar.gz
mailfromd-alpha_3_1_91_berkeley_txn.tar.bz2
git-svn-id: file:///svnroot/mailfromd/branches/alpha_3_1_91_berkeley_txn@1401 7a8a7f39-df28-0410-adc6-e0d955640f24
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--ChangeLog8
-rw-r--r--NEWS28
-rw-r--r--doc/mailfromd.texi566
-rw-r--r--doc/mtasim.texi8
-rw-r--r--gacopyz/gacopyz.h7
-rw-r--r--gacopyz/log.c20
-rw-r--r--gacopyz/smfi.c14
-rw-r--r--src/bi_db.m424
-rw-r--r--src/bi_io.m42
-rw-r--r--src/cache.c29
-rw-r--r--src/dnscache.c5
-rw-r--r--src/engine.c12
-rw-r--r--src/gram.y132
-rw-r--r--src/lex.l2
-rw-r--r--src/mailfromd.h6
-rw-r--r--src/main.c22
-rw-r--r--src/prog.c12
-rw-r--r--src/rate.c1
18 files changed, 718 insertions, 180 deletions
diff --git a/ChangeLog b/ChangeLog
index 3ac6ab6..6cd8427 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2007-05-01 Sergey Poznyakoff <gray@gnu.org.ua>
+
+ * src/lex.l, src/engine.c, src/dnscache.c, src/gram.y,
+ src/mailfromd.h, src/cache.c, src/prog.c, src/bi_io.m4,
+ src/main.c, src/rate.c, src/bi_db.m4, doc/mailfromd.texi,
+ doc/mtasim.texi, gacopyz/smfi.c, gacopyz/gacopyz.h, gacopyz/log.c,
+ NEWS: Port r1400 from trunk
+
2007-04-25 Sergey Poznyakoff <gray@gnu.org.ua>
Synchronize with the trunk
diff --git a/NEWS b/NEWS
index ee23f49..0bd3c44 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-Mailfromd NEWS -- history of user-visible changes. 2007-04-25
+Mailfromd NEWS -- history of user-visible changes. 2007-04-27
Copyright (C) 2005, 2006, 2007 Sergey Poznyakoff
See the end of file for copying conditions.
@@ -17,6 +17,32 @@ useful in automated test cases.
See the documentation, chapter `mtasim'.
+* `begin'/`end' handlers
+
+ The `begin' and `end' special handlers may be used to
+supply startup and cleanup code for the filter program.
+
+ The `begin' special handler is executed once for each
+SMTP session, after the connection has been established but
+before the first milter handler has been called. Similarly, an
+`end' handler is executed exactly once, after the connection has
+been closed. Neither of handlers takes any arguments.
+
+ See the documentation, section `begin/end'.
+
+* Cache control
+
+ Use function `db_set_active' to enable or disable given cache
+database. E.g.
+
+ # Disable DNS cache:
+ db_set_active("dns", 0)
+ # Enable it back again:
+ db_set_active("dns", 0)
+
+Similarly, the function `db_get_active' returns a number indicating
+whether the given cache database is used or not.
+
Version 3.1.91, 2007-04-23
diff --git a/doc/mailfromd.texi b/doc/mailfromd.texi
index 6ba9477..741499b 100644
--- a/doc/mailfromd.texi
+++ b/doc/mailfromd.texi
@@ -93,7 +93,7 @@ documents @command{mailfromd} Version @value{VERSION}.
* MFL:: The Mail Filtering Language.
* Mailfromd Configuration:: Configuring @command{mailfromd}.
* Sendmail Configuration:: Configuring Sendmail to use @command{mailfromd}.
-* mtasim:: MTA simulator.
+* mtasim:: An @acronym{MTA} simulator.
* Reporting Bugs:: How to Report a Bug.
Appendices
@@ -139,6 +139,7 @@ Tutorial
* Testing Filter Scripts::
* Logging and Debugging::
* Runtime errors::
+* Cautions::
Databases
@@ -157,7 +158,8 @@ Mail Filtering Language
* Constants::
* Variables::
* Back references::
-* Handlers::
+* Handlers::
+* begin/end::
* Functions:: Functions.
* Expressions:: Expressions.
* Statements::
@@ -383,14 +385,14 @@ always discarded.
The described method of address verification is called
@dfn{standard} method throughout this document. @command{Mailfromd}
also implements a method we call @dfn{strict}. When using strict
-method, @command{mailfromd} first resolves IP address of sender
+method, @command{mailfromd} first resolves @acronym{IP} address of sender
machine to a fully qualified domain name. Then it obtains MX records
for this machine, and then proceeds with probing as described above.
So, the difference between the two methods is in the set of MX
records that are being probed: standard method queries MXs based on
the sender email domain, strict method works with MXs for the sender
-IP address.
+@acronym{IP} address.
Strict method allows to cut off much larger amount of spam,
although it does have many drawbacks. Returning to our example above,
@@ -408,7 +410,7 @@ urge the remote party to retry sending his/her message later.
@cindex caching @acronym{DNS} requests
After receiving a definite answer, @command{mailfromd} will
cache it in its database, so that next time your @acronym{MTA} receives a
-message from that address (or from the sender IP/email address pair,
+message from that address (or from the sender @acronym{IP}/email address pair,
for strict method), it will not waste its time trying to reach MX
servers again. The records remain in the cache database for a certain
time, after which they are discarded.
@@ -436,7 +438,7 @@ the expiration timeout in your cache database.
@item When verifying the remote address, no attempt to actually
deliver the message is made. If @acronym{MTA} accepts the address,
@command{mailfromd} assumes it is OK. However in reality, mail for a
-remote address can bounce @emph{after} the nearest MTA accepts the
+remote address can bounce @emph{after} the nearest @acronym{MTA} accepts the
recipient address.
This drawback can often be avoided by combining sender address
@@ -993,7 +995,7 @@ more information about the database compaction.
This chapter contains a tutorial introduction, guiding you
through various @command{mailfromd} configurations, starting from the
-simplest ones and proceeding up to the most advanced forms. It omits
+simplest ones and proceeding up to more advanced forms. It omits
most complicated details, concentrating mainly on the
common practical tasks.
@@ -1020,6 +1022,7 @@ interaction with the Mail Transport Agent.
* Testing Filter Scripts::
* Logging and Debugging::
* Runtime errors::
+* Cautions::
@end menu
@node Start Up
@@ -1082,14 +1085,27 @@ be supplied its own handling procedure. A missing procedure implies
@cindex milter state handler, described
@cindex handler, described
+@cindex connect, handler
+@cindex helo, handler
+@cindex envfrom, handler
+@cindex envrcpt, handler
+@cindex header, handler
+@cindex eoh, handler
+@cindex body, handler
+@cindex eom, handler
+@cindex begin, special handler
+@cindex end, special handler
@anchor{handler names}
A filter script can define up to eight @dfn{milter state handlers},
called after the names of milter states: @samp{connect}, @samp{helo},
@samp{envfrom}, @samp{envrcpt}, @samp{header}, @samp{eoh},
-@samp{body}, and @samp{eom}. The diagram below shows the control flow
-when processing an @acronym{SMTP} transaction. Lines marked with
-@code{C:} show @acronym{SMTP} commands issued by the remote machine
-(the @dfn{client}), those marked with @samp{@result{}} show called handlers
+@samp{body}, and @samp{eom}. Two special handlers are available for
+initialization and cleran-up purposes: @samp{begin} is called before
+the processing starts, and @samp{end} is called after it is finished.
+The diagram below shows the control flow when processing an
+@acronym{SMTP} transaction. Lines marked with @code{C:} show
+@acronym{SMTP} commands issued by the remote machine (the
+@dfn{client}), those marked with @samp{@result{}} show called handlers
with their arguments. An @r{[R]} appearing at the right end of a line
indicates that this part of the transaction can be repeated any number
of times:
@@ -1098,6 +1114,7 @@ of times:
@caption{Mailfromd Control Flow}
@smallexample
@group
+@result{} begin
@result{} connect(@var{hostname}, @var{family}, @var{port}, @samp{IP address})
C: HELO @var{domain}
helo(@var{domain})
@@ -1124,7 +1141,8 @@ do
C: .
@result{} eom
- done
+ done
+@result{} end
@end group
@end smallexample
@end float
@@ -1301,6 +1319,7 @@ names of the formal parameters and the result type, if the function is
to return a meaningful value (function definitions in @acronym{MFL}
are discussed in detail in @pxref{User-defined, User-Defined Functions}).
+@anchor{funcall}
@cindex function calls
A function is invoked using a special construct, @dfn{function
call}:
@@ -1332,7 +1351,7 @@ hostname $client_addr
@noindent
However, such syntax creates several ambiguities, so use it sparingly
if at all. We recommend to always use parentheses when calling a
-function. @FIXME{explain why.}
+function; @xref{Cautions}, for the detailed analysis of of this syntax.
When a function does not deliver a result, it should only be called
as a statement.
@@ -1355,7 +1374,7 @@ designed for a particular task. In order to access a library
function, you must first @dfn{require} a module it is defined in.
This is done using @code{#require} statement. For example, the
function @code{hostname} looks up in the @acronym{DNS} the name
-corresponding to the IP address specified as its argument. This
+corresponding to the @acronym{IP} address specified as its argument. This
function is defined in module @file{dns.mf}, so before calling it you
must require this module:
@@ -1377,9 +1396,9 @@ module on disk and loads it if it is available.
do not have a proper reverse delegation in the Domain Name System.
In the previous section we introduced the library function
@code{hostname}, that looks up in the @acronym{DNS} the name corresponding to
-the IP address specified as its argument. If there is no
+the @acronym{IP} address specified as its argument. If there is no
corresponding name, the function returns its argument unchanged. This
-can be used to test if the IP was resolved, as illustrated in the
+can be used to test if the @acronym{IP} was resolved, as illustrated in the
example below:
@smallexample
@@ -1399,7 +1418,7 @@ done
after which the definition of @code{hostname} becomes available.
An orthogonal function, @code{resolve}, which resolves the symbolic
-name to the corresponding IP address is provided in the same
+name to the corresponding @acronym{IP} address is provided in the same
@file{dns.mf} module.
@node Checking Sender Address
@@ -1486,10 +1505,10 @@ done
@end smallexample
@node SMTP Timeouts
-@section SMTP Timeouts
+@section @acronym{SMTP} Timeouts
When using polling functions, it is important to take into account
-possible delay, which can occur in SMTP transactions. Most often
+possible delay, which can occur in @acronym{SMTP} transactions. Most often
such delays are due to low network bandwidth, but sometimes remote
sites impose them willingly, as a spam-fighting measure@footnote{My
private opinion is that such practice is completely lame.}
@@ -1628,8 +1647,8 @@ done
Another way to avoid infinite looping caused by endless recursive
triggering of @code{on poll}, is to accept relaying of all email originating
-from the local IP cluster with (trusted) clients and SMTP servers,
-provided that the server running mailfromd falls within this IP range:
+from the local @acronym{IP} cluster with (trusted) clients and @acronym{SMTP} servers,
+provided that the server running mailfromd falls within this @acronym{IP} range:
@smallexample
@group
@@ -1654,12 +1673,12 @@ done
@end smallexample
Here, triggering the @code{on poll} statement with more than 1 recursion
-is avoided for all local emails, originating from non-local IPs
-(outside of CIDR range 193.232.0.0/16) - when such an email arrives,
+is avoided for all local emails, originating from non-local @acronym{IP}s
+(outside of @acronym{CIDR} range 193.232.0.0/16) - when such an email arrives,
handler execution falls through to @code{on poll}, which will cause
the server to connect back to itself for local email verification, but
this time, the @code{on poll} check will be skipped, as the server's
-own IP address will be caught by @code{match_cidr} statement.
+own @acronym{IP} address will be caught by @code{match_cidr} statement.
This method has particular advantage over the previous one: it
does not rely on sendmail's relay-domains control, which can be,
alone, too wide for sane relaying control.
@@ -1916,7 +1935,7 @@ interval, which is an integer number. For example, the number
updates the rate record for the given @var{key}, and returns its
value, converted to messages per interval. For example, the following
code limits the mail sending rate for each @samp{email
-address}-@samp{IP} combination to 180 per hour. If the rate value is
+address}-@samp{@acronym{IP}} combination to 180 per hour. If the rate value is
exceeded, the sender is returned a temporary failure response:
@smallexample
@@ -1951,7 +1970,7 @@ intervals are discussed in @ref{time interval specification}.
Greylisting is a simple method of defending against the spam
proposed by Evan Harris. In few words, it consists in recording the
-@samp{sender IP}-@samp{sender email}-@samp{recipient email} triplet of
+@samp{sender @acronym{IP}}-@samp{sender email}-@samp{recipient email} triplet of
mail transactions. Each time the unknown triplet is seen, the
corresponding message is rejected with @code{tempfail} code. If the
mail is legitimate, this will make the originating server will retry
@@ -2020,7 +2039,7 @@ done
@end smallexample
In real life you will have to avoid greylisting some messages, in
-particular those coming from the @samp{<>} address and from the IP
+particular those coming from the @samp{<>} address and from the @acronym{IP}
addresses in your relayed domain. It can easily be done using the
techniques described in previous sections and is left as an exercises
to the reader.
@@ -2196,7 +2215,7 @@ described in the following table:
@table @asis
@item A
- Each field contains the next IP address corresponding to the lookup
+ Each field contains the next @acronym{IP} address corresponding to the lookup
key. Notice, that currently (version @value{VERSION}) there can be at
most one field here, but it may change in the future.
@@ -2913,6 +2932,104 @@ the error and fix it.
calling the @code{stack_trace} function. This can be useful for
debugging, or in your @code{catch} statements.
+@node Cautions
+@section Warnings about some slippery places in @acronym{MFL}
+
+@quotation
+It seemed like a good idea at the time.
+
+--- Brian Kernighan
+@end quotation
+
+ There are some features of @acronym{MFL} which, when used improperly,
+may lead to subtle, hard identifiable errors. These are: concatenation
+operation (@pxref{Concatenation}) and passing arguments to
+one-argument functions without parentheses (@pxref{funcall, Function
+call syntax}).
+
+ Since there is no explicit operator for concatenation, it is often
+necessary to ensure that it happens at the right time by using
+parentheses to enclose the items to concatenate. Consider the
+following example:
+
+@smallexample
+echo toupper "some" "thing"
+@end smallexample
+
+ Should it print @samp{SOMETHING} or just @samp{SOMEthing}? The
+correct answer is the former, but it is difficult to deduce unless you
+are well acquainted with the @acronym{MFL} precedence rules
+(@pxref{Precedence}). Therefore, the rule of thumb is: whenever in
+doubt, parenthesize:
+
+@smallexample
+echo toupper("some" "thing") @result{} "SOMETHING"
+echo toupper("some") "thing" @result{} "SOMEthing"
+@end smallexample
+
+ Quoteless literals (@pxref{Literals}) are yet another dangerous
+feature. Just as the features mentioned above, it stems from the
+good old days when @acronym{MFL} was small and sweet and using
+literals without quotes indeed ``seemed a good idea at the time.'' It
+ceased to seem so after the introduction of user-defined functions,
+though. Consider the following @emph{entire} program text:
+
+@smallexample
+@group
+prog envfrom
+do
+ if hostname($client_addr) = $client_addr
+ reject
+ fi
+done
+@end group
+@end smallexample
+
+ The intent was obviously to reject any mail if it comes from an
+address without a proper @code{PTR} record (@pxref{hostname
+function}). There is a serious error, however: @code{hostname} is not
+a built-in function as it used to be in previous releases@footnote{Up to
+the version 1.3.91.}, and therefore it needs to be defined or required
+prior to using. Otherwise it is no more than a literal, and the whole
+construct @samp{hostname($client_addr)} is regarded by @acronym{MFL}
+compiler as a concatenation of the string @samp{hostname} and the
+value of @samp{client_addr} Sendmail variable. It is easy to see
+using @option{--dump-tree} option:
+
+@smallexample
+$ @kbd{mailfromd --dump-tree test.mf}
+State handlers:
+---------------
+envfrom:
+COND:
+EQ
+ CONCAT:
+ STRING: "hostname"
+ SYMBOL: @{client_addr@}
+ SYMBOL: @{client_addr@}
+IFTRUE
+ reject
+IFFALSE
+@end smallexample
+
+ In effect, the comparison is always false and @code{reject} is never
+called.
+
+ That is why starting from version 3.0 @command{mailfromd} warns
+about any occurrence of an unquoted identifier. In fact, running
+@option{--lint} on the above program, gives:
+
+@smallexample
+$ @kbd{mailfromd --lint test.mf}
+mailfromd: test.mf:3: warning: unquoted identifier `hostname'
+@end smallexample
+
+ Whenever you see such a message, be sure to inspect the source and
+to place quotes around the suspicious string, if it is intended to be
+used as a literal, or to require the corresponding module
+(@pxref{Modules}) (or include the source file directly,
+@pxref{include}), if it is indeed a function name.
+
@node MFL, Mailfromd Configuration, Tutorial, Top
@chapter Mail Filtering Language
@cindex MFL
@@ -2938,7 +3055,8 @@ amount of white-space characters (i.e. spaces, tabulations or newlines).
* Constants::
* Variables::
* Back references::
-* Handlers::
+* Handlers::
+* begin/end::
* Functions:: Functions.
* Expressions:: Expressions.
* Statements::
@@ -3127,7 +3245,7 @@ a true value, and @code{no}, @code{false} or @code{nil} to indicate a
false value.
@item address
- An IP address in ``dotted-quad'' notation or a fully-qualified host
+ An @acronym{IP} address in ``dotted-quad'' notation or a fully-qualified host
name.
@item interval
@@ -3200,7 +3318,7 @@ detailed description.
@deffn {pragma option} initial-response-timeout @var{interval}
@xprindex{initial-response-timeout}
- Sets the time to wait for the initial SMTP response. Default is
+ Sets the time to wait for the initial @acronym{SMTP} response. Default is
@value{INITIAL-RESPONSE-TIMEOUT}. @xref{SMTP Timeouts}, for
the detailed description.
@end deffn
@@ -3358,7 +3476,7 @@ port type is not yet supported.
@deffn {pragma option} milter-timeout @var{interval}
@xprindex{milter-timeout}
Set the timeout value for connection between the filter
-and the MTA. Default value is @value{MILTER-TIMEOUT}. You
+and the @acronym{MTA}. Default value is @value{MILTER-TIMEOUT}. You
normally do not need to change this value.
@end deffn
@@ -4142,7 +4260,7 @@ qualified domain name of the host where @command{mailfromd} is run.
@deftypevar {Predefined Variable} string last_poll_host
Polling functions (@pxref{Polling functions}) set this variable before
-returning. It contains the host name or IP address of the last polled host.
+returning. It contains the host name or @acronym{IP} address of the last polled host.
@end deftypevar
@deftypevar {Predefined Variable} string last_poll_recv
@@ -4292,14 +4410,14 @@ the available handlers and their arguments:
@deffn {Handler} connect (string $1, number $2, number $3, string $4)
@subsubheading Invocation
-This handler is called once at the beginning of each SMTP connection.
+This handler is called once at the beginning of each @acronym{SMTP} connection.
@subsubheading Arguments
@enumerate 1
@item @code{string};
-The host name of the message sender, as reported by MTA. Usually it
+The host name of the message sender, as reported by @acronym{MTA}. Usually it
is determined by a reverse lookup on the host address. If the reverse
-lookup fails, @samp{$1} will contain the message sender's IP address
+lookup fails, @samp{$1} will contain the message sender's @acronym{IP} address
enclosed in square brackets (e.g. @samp{[127.0.0.1]}).
@item @code{number};
@@ -4311,17 +4429,17 @@ definitions for the address families. Supported families are:
@cindex FAMILY_INET
@multitable @columnfractions 0.20 .10 0.70
@headitem Constant @tab Value @tab Meaning
-@item FAMILY_STDIO @tab 0 @tab Standard input/output (the MTA is
+@item FAMILY_STDIO @tab 0 @tab Standard input/output (the @acronym{MTA} is
run with @option{-bs} option)
@item FAMILY_UNIX @tab 1 @tab @acronym{UNIX} socket
-@item FAMILY_INET @tab 2 @tab IPv4 protocol
+@item FAMILY_INET @tab 2 @tab @acronym{IP}v4 protocol
@end multitable
@item @code{number};
Port number if @samp{$2} is @samp{FAMILY_INET}.
@item @code{string};
-Remote IP address if @samp{$2} is @samp{FAMILY_INET} or full file name
+Remote @acronym{IP} address if @samp{$2} is @samp{FAMILY_INET} or full file name
of the socket if @samp{$2} is @samp{FAMILY_UNIX}. If @samp{$2} is
@samp{FAMILY_STDIO}, @samp{$4} is an empty string.
@end enumerate
@@ -4374,9 +4492,9 @@ command, excepting ones listed above, is answered with
2821 (section 3.9), which states:
@quotation
- An SMTP server MUST NOT intentionally close the connection except:
+ An @acronym{SMTP} server @emph{must not} intentionally close the connection except:
@dots{}
- - After detecting the need to shut down the SMTP service and
+ - After detecting the need to shut down the @acronym{SMTP} service and
returning a 421 response code. This response code can be issued
after the server receives any command or, if necessary,
asynchronously from command receipt (on the assumption that the
@@ -4399,8 +4517,8 @@ versions of Sendmail up to 8.14.
@deffn {Handler} helo (string $1)
@subsubheading Invocation
-This handler is called whenever the SMTP client sends @code{HELO} or
-@code{EHLO} command. Depending on the actual MTA configuration, it
+This handler is called whenever the @acronym{SMTP} client sends @code{HELO} or
+@code{EHLO} command. Depending on the actual @acronym{MTA} configuration, it
can be called several times or even not at all.
@subsubheading Arguments
@@ -4410,7 +4528,7 @@ can be called several times or even not at all.
@subsubheading Notes
According to @acronym{RFC} 28221, @code{$1} must be the domain name of the
-sending host, or, in case this is not available, its IP address
+sending host, or, in case this is not available, its @acronym{IP} address
enclosed in square brackets. Be careful when taking decisions based
on this value, because in practice many hosts send arbitrary strings.
We recommend to use @code{heloarg_test} function
@@ -4419,7 +4537,7 @@ We recommend to use @code{heloarg_test} function
@deffn {Handler} envfrom (string $1, string $2)
@subsubheading Invocation
-Called when the SMTP client sends @code{MAIL FROM} command, i.e. once
+Called when the @acronym{SMTP} client sends @code{MAIL FROM} command, i.e. once
at the beginning of each message.
@subsubheading Arguments
@@ -4459,7 +4577,7 @@ an array of arguments.
@deffn {Handler} header (string $1, string $2)
@subsubheading Invocation
-Called once for each header line received after SMTP @code{DATA} command.
+Called once for each header line received after @acronym{SMTP} @code{DATA} command.
@subsubheading Arguments
@enumerate 1
@item @code{string}; Header field name.
@@ -4526,7 +4644,155 @@ For your reference, the following table shows each handler with its arguments:
@end multitable
@end float
+@node begin/end
+@section The @samp{begin} and @samp{end} special handlers
+@cindex begin, special handler
+@cindex end, special handler
+@cindex startup handler
+@cindex handler, startup
+@cindex handler, initialization
+@cindex cleanup handler
+@cindex handler, cleanup
+ Apart from milter handlers defined previously, @acronym{MFL}
+defines two special handlers, called @samp{begin} and @samp{end},
+which supply startup and cleanup instructions for the filter program.
+
+ The @samp{begin} special handler is executed once for each
+@acronym{SMTP} session, after the connection has been established but
+before the first milter handler has been called. Similarly, an
+@samp{end} handler is executed exactly once, after the connection has
+been closed. Neither of handlers takes any arguments.
+
+@kwindex begin
+@kwindex end
+ The two handlers are defined using the following syntax:
+
+@smallexample
+# @r{Begin handler}
+begin
+do
+ @dots{}
+done
+
+# @r{End handler}
+end
+do
+ @dots{}
+done
+@end smallexample
+
+@noindent
+where @samp{@dots{}} represent any @acronym{MFL} statements.
+
+ An @acronym{MFL} program may have multiple @samp{begin} and
+@samp{end} definitions. They can be intermixed with other
+definitions. The compiler combines all @samp{begin}
+statements into a single one, in the order they appear in the
+sources. Similarly, all @samp{end} blocks are concatenated together.
+The resulting @samp{begin} is called once, at the beginning of each
+@acronym{SMTP} session, and @samp{end} is called once at its
+termination.
+
+ Multiple @samp{begin} and @samp{end} handlers are a useful feature
+for writing modules (@pxref{Modules}), because each module can thus
+have its own initialization and cleanup blocks. Notice, however, that
+in this case the order in which subsequent @samp{begin} and @samp{end}
+blocks are executed is not defined. It is only warranted that all
+@samp{begin} blocks are executed at startup and all @samp{end} blocks
+are executed at shutdown. It is also warranted that all @samp{begin}
+and @samp{end} blocks defined within a compilation unit (i.e. a single
+abstract source file, whith all @code{#include} and
+@code{#include_once} statements expanded in place) are executed in
+order of their appearance in the unit.
+
+@cindex @samp{begin}, handler restrictions
+@cindex @samp{end}, handler restrictions
+ Due to their special nature, the startup and cleanup blocks impose
+certain restrictions on the statements that can be used within them:
+
+@enumerate 1
+@cindex @code{return} in @samp{begin}
+@cindex @samp{begin} and @code{return}
+@cindex @code{return} in @samp{end}
+@cindex @samp{end} and @code{return}
+@item @code{return} cannot be used in @samp{begin} and @samp{end}
+handlers. @FIXME{It could be a useful feature, though.}
+
+@cindex @code{accept} in @samp{begin}
+@cindex @samp{begin} and @code{accept}
+@cindex @code{accept} in @samp{end}
+@cindex @samp{end} and @code{accept}
+@cindex @code{continue} in @samp{begin}
+@cindex @samp{begin} and @code{continue}
+@cindex @code{continue} in @samp{end}
+@cindex @samp{end} and @code{continue}
+@cindex @code{discard} in @samp{begin}
+@cindex @samp{begin} and @code{discard}
+@cindex @code{discard} in @samp{end}
+@cindex @samp{end} and @code{discard}
+@cindex @code{reject} in @samp{begin}
+@cindex @samp{begin} and @code{reject}
+@cindex @code{reject} in @samp{end}
+@cindex @samp{end} and @code{reject}
+@cindex @code{tempfail} in @samp{begin}
+@cindex @samp{begin} and @code{tempfail}
+@cindex @code{tempfail} in @samp{end}
+@cindex @samp{end} and @code{tempfail}
+@item The following Sendmail actions cannot be used in them:
+@code{accept}, @code{continue}, @code{discard}, @code{reject},
+@code{tempfail}. They can, however, be used in @code{cache}
+statements, declared in @samp{begin} blocks (see example below).
+
+@cindex @code{add} in @samp{begin}
+@cindex @samp{begin} and @code{add}
+@cindex @code{add} in @samp{end}
+@cindex @samp{end} and @code{add}
+@cindex @code{replace} in @samp{begin}
+@cindex @samp{begin} and @code{replace}
+@cindex @code{replace} in @samp{end}
+@cindex @samp{end} and @code{replace}
+@cindex @code{delete} in @samp{begin}
+@cindex @samp{begin} and @code{delete}
+@cindex @code{delete} in @samp{end}
+@cindex @samp{end} and @code{delete}
+@item Header manipulation actions (@pxref{header manipulation}) can be
+used only in @samp{begin} header.
+@end enumerate
+
+ The @samp{begin} handlers are the usual place to put global
+initialization code to. For example, if you do not want to use
+@acronym{DNS} caching, you can do it this way:
+
+@smallexample
+@group
+begin
+do
+ db_set_active("dns", 0)
+done
+@end group
+@end smallexample
+ Additionally, you can set up global exception handling routines
+there. For example, the following @samp{begin} statement disables
+@acronym{DNS} cache and for all exceptions not handled otherwise
+installs a handler that logs the exception along with the stack trace
+and continues processing the message:
+
+@smallexample
+@group
+begin
+do
+ db_set_active("dns", 0)
+ catch *
+ do
+ echo "Caught exception $1: $2"
+ stack_trace()
+ continue
+ done
+done
+@end group
+@end smallexample
+
@node Functions
@section Functions
@@ -4769,7 +5035,7 @@ strip_domain_part("puszcza.gnu.org.ua", 0) @result{} "gnu.org.ua"
@deftypefn {Library Function} boolean is_ip (string @var{str})
@flindex is_ip.mf
- Returns @samp{true} if @var{str} is a valid IPv4 address. This
+ Returns @samp{true} if @var{str} is a valid @acronym{IP}v4 address. This
function is defined in @file{is_ip.mf} module (@pxref{Modules}).
For example:
@@ -4790,7 +5056,7 @@ is_ip("0.0.0.0") @result{} 1
@deftypefn {Library Function} string revip (string @var{ip})
Reverses octets in @var{ip}, which must be a valid string
-representation of an IPv4 address.
+representation of an @acronym{IP}v4 address.
Example:
@@ -4950,7 +5216,7 @@ variables:
@multitable @columnfractions 0.30 0.70
@headitem Variable @tab Contains
@cindex last_poll_host, global variable, introduced
-@item last_poll_host @tab Host name or IP address of the last polled
+@item last_poll_host @tab Host name or @acronym{IP} address of the last polled
host.
@cindex last_poll_send, global variable, introduced
@item last_poll_send @tab Last @acronym{SMTP} command, sent to this
@@ -4970,7 +5236,7 @@ variables are modified. @xref{cache_used example}, for an example.
@node Internet address manipulation functions
@subsubsection Internet address manipulation functions
- Following functions operate on IPv4 addresses and CIDRs.
+ Following functions operate on @acronym{IP}v4 addresses and @acronym{CIDR}s.
@deftypefn {Built-in Function} number ntohl (number @var{n})
Converts the number @var{n}, from host to network byte order.
@@ -5009,7 +5275,7 @@ negative number:}
inet_aton("255.255.255.255") @result{} -1
@end smallexample
-However, this does not affect arithmetical operations on IP addresses.
+However, this does not affect arithmetical operations on @acronym{IP} addresses.
@end deftypefn
@@ -5023,7 +5289,7 @@ inet_ntoa(2130706433) @result{} "127.0.0.1"
@end deftypefn
@deftypefn {Built-in Function} number len_to_netmask (number @var{n})
-Convert number of masked bits @var{n} to the IPv4 netmask:
+Convert number of masked bits @var{n} to the @acronym{IP}v4 netmask:
@smallexample
inet_ntoa(len_to_netmask(24)) @result{} 255.255.255.0
@@ -5035,7 +5301,7 @@ exception.
@end deftypefn
@deftypefn {Built-in Function} number netmask_to_len (number @var{mask})
-Convert IPv4 netmask @var{mask} into netmask length (number of bits
+Convert @acronym{IP}v4 netmask @var{mask} into netmask length (number of bits
preserved by the mask):
@smallexample
@@ -5052,15 +5318,15 @@ netmask_to_len(inet_aton("254.0.0.0")) @result{} 7
This function is defined in @file{match_cidr.mf}
module (@pxref{Modules}).
- It returns @code{true} if the IP address @var{ip} pertains to the
-IP range @var{cidr}. The first argument, @var{ip}, is a string
-representation of an IP address. The second argument, @var{cidr}, is
-a string representation of a IP range in @acronym{CIDR} notation, i.e.
-@code{"@var{A.B.C.D}/@var{N}"}, where @var{A.B.C.D} is an IPv4
+ It returns @code{true} if the @acronym{IP} address @var{ip} pertains to the
+@acronym{IP} range @var{cidr}. The first argument, @var{ip}, is a string
+representation of an @acronym{IP} address. The second argument, @var{cidr}, is
+a string representation of a @acronym{IP} range in @acronym{CIDR} notation, i.e.
+@code{"@var{A.B.C.D}/@var{N}"}, where @var{A.B.C.D} is an @acronym{IP}v4
address and @var{N} specifies the @dfn{prefix length} -- the number of
shared initial bits, counting from the left side of the address.
- The following example will reject the mail if the IP address of
+ The following example will reject the mail if the @acronym{IP} address of
the sending machine does not belong to the block @code{10.10.1.0/19}:
@smallexample
@@ -5095,7 +5361,7 @@ available after requesting @file{dns.mf} module (@pxref{Modules}):
@end smallexample
@deftypefn {Built-in Function} string dns_getaddr (string @var{domain})
- Returns a whitespace-separated list of IP addresses (@code{A}
+ Returns a whitespace-separated list of @acronym{IP} addresses (@code{A}
records) for @var{domain}. At most 64 addresses are
returned. @FIXME{This limit should be configurable.}
@@ -5104,7 +5370,7 @@ returned. @FIXME{This limit should be configurable.}
@deftypefn {Built-in Function} string dns_getname (string @var{ipstr})
Returns a whitespace-separated list of domain names (@code{PTR}
-records) for the IPv4 address @var{ipstr}. At most 64 names are
+records) for the @acronym{IP}v4 address @var{ipstr}. At most 64 names are
returned. @FIXME{This limit should be configurable.}
This function does not use the @acronym{DNS} cache.
@@ -5113,7 +5379,7 @@ returned. @FIXME{This limit should be configurable.}
@deftypefn {Built-in Function} string getmx (string @var{domain} @
[, number @var{resolve}])
Returns a whitespace-separated list of MX names (if @var{resolve} is not
-given or if it is @code{0}) or MX IP addresses (if
+given or if it is @code{0}) or MX @acronym{IP} addresses (if
@code{@var{resolve}!=0})) for @var{domain}. Within the returned
strings, items are sorted lexicographically. If @var{domain} has no
MX records, empty string is returned. If the @acronym{DNS} query fails,
@@ -5130,7 +5396,7 @@ mxof("org.pl") @result{} ""
@emph{Notes}:
@enumerate 1
-@item The @code{getmx} function returns at most 32 MX names or IP addresses.
+@item The @code{getmx} function returns at most 32 MX names or @acronym{IP} addresses.
@FIXME{This limit should probably be configurable.}
@item The number of items returned by @code{getmx(@var{domain})} can
@@ -5175,9 +5441,9 @@ has any MX records.
@end deftypefn
@deftypefn {Built-in Function} string primitive_hostname (string @var{ip})
- The @var{ip} argument should be a string representing an IP address in
+ The @var{ip} argument should be a string representing an @acronym{IP} address in
@dfn{dotted-quad} notation. The function returns the canonical name of
-the host with this IP address obtained from @acronym{DNS} lookup. For example
+the host with this @acronym{IP} address obtained from @acronym{DNS} lookup. For example
@smallexample
primitive_hostname $@{client_addr@}
@@ -5194,16 +5460,17 @@ raises the exception @code{not_found}.
@code{temp_failure}, depending on the character of the failure.
@end deftypefn
+@anchor{hostname function}
@deftypefn {Library Function} string hostname (string @var{ip})
- The @var{ip} argument should be a string representing an IP address in
+ The @var{ip} argument should be a string representing an @acronym{IP} address in
@dfn{dotted-quad} notation. The function returns the canonical name of
-the host with this IP address obtained from @acronym{DNS} lookup.
+the host with this @acronym{IP} address obtained from @acronym{DNS} lookup.
If there is no PTR record for @var{ip}, or if the lookup fails,
the function returns @var{ip} unchanged.
The previous @command{mailfromd} versions used the following
-paradigm to check if an IP address resolves:
+paradigm to check if an @acronym{IP} address resolves:
@smallexample
if hostname(%ip) != %ip
@@ -5216,7 +5483,7 @@ paradigm to check if an IP address resolves:
@deftypefn {Built-in Function} boolean primitive_ismx (string @var{domain}, @
string @var{host})
The @var{domain} argument is any valid domain name, the @var{host}
-is a host name or IP address.
+is a host name or @acronym{IP} address.
The function returns @code{true} if @var{host} is one of the MX
records for the @var{domain}.
@@ -5232,7 +5499,7 @@ exception @code{not_found}.
@deftypefn {Library Function} boolean ismx (string @var{domain}, @
string @var{host})
The @var{domain} argument is any valid domain name, the @var{host}
-is a host name or IP address.
+is a host name or @acronym{IP} address.
The function returns @code{true} if @var{host} is one of the MX
records for the @var{domain}. Otherwise it returns @code{false}.
@@ -5244,7 +5511,7 @@ function returns @code{false}.
@deftypefn {Built-in Function} string primitive_resolve (string @var{host}, @
[string @var{domain}])
Reverse of @code{primitive_hostname}. The @code{primitive_resolve} function
-returns the IP address for the host name specified by @var{host}
+returns the @acronym{IP} address for the host name specified by @var{host}
argument. If @var{host} has no A records, the function raises the
exception @code{not_found}.
@@ -5282,7 +5549,7 @@ another practical example of the use of the two-argument form.
@deftypefn {Library Function} string resolve (string @var{host}, @
[string @var{domain}])
Reverse of @code{hostname}. The @code{resolve} function
-returns the IP address for the host name specified by @var{host}
+returns the @acronym{IP} address for the host name specified by @var{host}
argument. If the host name cannot be resolved, or a @acronym{DNS} failure
occurs, the function returns @samp{"0"}.
@@ -5368,7 +5635,7 @@ A mx10.gnu.org: Sun Dec 3 10:56:12 2006 199.232.76.166
@end group
@end smallexample
- The above example shows that the IP address of @samp{mx10.gnu.org}
+ The above example shows that the @acronym{IP} address of @samp{mx10.gnu.org}
and that it expires on Sunday, December 3d, at 10:56:12.
Of course, the rest of database management options are also valid
@@ -5478,6 +5745,26 @@ for that format. If @var{fmtid} does not match any known format,
@code{db_name} raises the @code{not_found} exception.
@end deftypefn
+@cindex getting cache status
+@cindex cache, getting status
+@deftypefn {Built-in Function} number db_get_active (string @var{fmtid})
+ Returns the flag indicating whether the cache database @var{fmtid}
+is currently enabled. If @var{fmtid} does not match any known format,
+@code{db_name} raises the @code{not_found} exception.
+@end deftypefn
+
+@cindex disabling cache
+@cindex cache, disabling
+@deftypefn {Built-in Function} void db_set_active (string @var{fmtid}, number @var{enable})
+ Enables the cache database @var{fmtid} if @var{enable} is not null,
+or disables it otherwise. For example, to disable @acronym{DNS}
+caching, do:
+
+@smallexample
+db_set_active("dns", 0)
+@end smallexample
+@end deftypefn
+
@deftypefn {Built-in Function} boolean relayed (string @var{domain})
@anchor{relayed}
Returns @code{true} if the string @var{domain} is found in one of
@@ -5489,7 +5776,7 @@ relayed hostname $@{client_addr@}
@end smallexample
@noindent
-which yields @code{true} if the IP address from @command{Sendmail} variable
+which yields @code{true} if the @acronym{IP} address from @command{Sendmail} variable
@samp{client_addr} is relayed by the local machine.
@end deftypefn
@@ -5575,7 +5862,7 @@ exception if an I/O error occurs.
@end deftypefn
The following example shows how @command{mailfromd} I/O functions can
-be used to automatically add IP addresses to an @acronym{RBL} zone:
+be used to automatically add @acronym{IP} addresses to an @acronym{RBL} zone:
@smallexample
@group
@@ -5671,7 +5958,7 @@ The @var{proto} part specifies the @dfn{connection protocol}. It
should be @samp{tcp} for the @acronym{TCP} connection and @samp{file}
or @samp{socket} for the connection via UNIX socket. In the latter
case the @var{proto} part can be omitted. When using @acronym{TCP}
-connection, the @var{path} part gives the remote host name or IP
+connection, the @var{path} part gives the remote host name or @acronym{IP}
address and the optional @var{port} specifies the port number or
service name to use. For example:
@@ -5824,7 +6111,7 @@ of greylisting period. @xref{Greylisting}, for the detailed explanation.
@end deftypefn
@deftypefn {Built-in Function} boolean listens (string @var{host}, [number @var{port}])
- Returns @code{true} if the IP address or host name given by
+ Returns @code{true} if the @acronym{IP} address or host name given by
@var{host} argument listens on the port number @var{port} (default 25).
@end deftypefn
@@ -5877,11 +6164,11 @@ Arguments:
Sendmail macro;
@item remote_ip
-IP address of the remote client. Typically, the value of
+@acronym{IP} address of the remote client. Typically, the value of
@code{$client_addr} Sendmail macro;
@item local_ip
-IP address of this @acronym{SMTP} server;
+@acronym{IP} address of this @acronym{SMTP} server;
@end table
The function returns a number describing the result of the test, as
@@ -5890,17 +6177,17 @@ described in the following table.
@multitable @columnfractions 0.4 0.6
@headitem Code @tab Meaning
@item HELO_SUCCESS @tab @var{arg} successfully passes all tests.
-@item HELO_MYIP @tab @var{arg} is our IP address.
-@item HELO_IPNOMATCH @tab @var{arg} is an IP, but it does not match
-the remote party IP address.
-@item HELO_ARGNORESOLVE @tab @var{arg} is an IP, but it does not resolve.
-@item HELO_ARGNOIP @tab @var{arg} is in square brackets, but it is not an IP
+@item HELO_MYIP @tab @var{arg} is our @acronym{IP} address.
+@item HELO_IPNOMATCH @tab @var{arg} is an @acronym{IP}, but it does not match
+the remote party @acronym{IP} address.
+@item HELO_ARGNORESOLVE @tab @var{arg} is an @acronym{IP}, but it does not resolve.
+@item HELO_ARGNOIP @tab @var{arg} is in square brackets, but it is not an @acronym{IP}
address.
-@item HELO_ARGINVALID @tab @var{arg} is not an IP address and does not
+@item HELO_ARGINVALID @tab @var{arg} is not an @acronym{IP} address and does not
resolve to one.
-@item HELO_MYSERVERIP @tab @var{arg} resolves to our server IP.
+@item HELO_MYSERVERIP @tab @var{arg} resolves to our server @acronym{IP}.
@item HELO_IPMISMATCH @tab @var{arg} does not resolve to the remote
-client IP address.
+client @acronym{IP} address.
@end multitable
@end deftypefn
@@ -5958,7 +6245,7 @@ mailer.
The @samp{smtp} protocol means to use an @acronym{SMTP} server directly.
In this case the mailer location consists of two slashes,
-the IP address or host name of the @acronym{SMTP} server, and,
+the @acronym{IP} address or host name of the @acronym{SMTP} server, and,
optionally, the port number. For example:
@smallexample
@@ -6097,7 +6384,7 @@ Sender address.
@subsubsection Blacklisting Functions
The functions described in this subsection allow to check whether the
-given IP address is listed in certain @dfn{black list} @acronym{DNS}
+given @acronym{IP} address is listed in certain @dfn{black list} @acronym{DNS}
zone.
@anchor{match_dnsbl}
@@ -6106,7 +6393,7 @@ zone.
This function looks up the @var{address} in the @acronym{DNS}
blacklist zone @var{zone} and checks if the return matches
-given @var{range} of IP addresses.
+given @var{range} of @acronym{IP} addresses.
It is intended as a replacement for the Sendmail macros @samp{dnsbl} and
@samp{enhdnsbl}.
@@ -6119,18 +6406,18 @@ Arguments:
@table @var
@item address
-IP address of the @acronym{SMTP} server to be tested.
+@acronym{IP} address of the @acronym{SMTP} server to be tested.
@item zone
@acronym{FQDN} of the @acronym{DNS}bl zone to test against.
@item range
-The range of IP addresses in @acronym{CIDR} notation or
+The range of @acronym{IP} addresses in @acronym{CIDR} notation or
the word @samp{ANY}, which stands for @samp{127.0.0.0/8}.
@end table
The function returns @code{true} if dns lookup for @var{address} in
-the zone @var{dnsbl} yields an IP that falls within the range,
+the zone @var{dnsbl} yields an @acronym{IP} that falls within the range,
specified by @var{cidr}. Otherwise, it returns @code{false}. If
any of @var{address} or @var{cidr} is invalid, @code{match_dnsbl}
returns @code{false}.
@@ -6140,10 +6427,10 @@ returns @code{false}.
@deftypefn {Library Function} boolean match_rhsbl (string @var{email}, @
string @var{zone}, string @var{range})
-This function checks if the IP address, corresponding to the domain
+This function checks if the @acronym{IP} address, corresponding to the domain
part of @var{email} is listed in the @acronym{RHS DNS} blacklist zone
@var{zone}, and if so, whether its record matches the given range of
-IP addresses @var{range}.
+@acronym{IP} addresses @var{range}.
It is intended as a replacement for the Sendmail macro @samp{rhsbl}
by Derek J. Balling.
@@ -6162,7 +6449,7 @@ E-mail address, whose domain name should be tested (usually, it is
Domain name of the @acronym{RHS DNS} blacklist zone.
@item range
-The range of IP addresses in @acronym{CIDR} notation.
+The range of @acronym{IP} addresses in @acronym{CIDR} notation.
@end table
@end deftypefn
@@ -6201,7 +6488,7 @@ and analyzing its return code. The function can be called either in
@table @var
@item ip
- The IP address of the SMTP client that is emitting the mail.
+ The @acronym{IP} address of the @acronym{SMTP} client that is emitting the mail.
Usually it is @code{$client_addr}.
@item domain
@@ -6236,7 +6523,7 @@ further checks that will decide about its fate.
@cindex Neutral, SPF result code
@item Neutral
The domain owner has explicitly stated that he cannot or does not
-want to assert whether or not the IP address is authorized. This
+want to assert whether or not the @acronym{IP} address is authorized. This
result must be treated exactly like @code{None}; the distinction
between them exists only for informational purposes
@@ -6355,7 +6642,7 @@ result code}.
@table @var
@item ip
- The IP address of the SMTP client that is emitting the mail.
+ The @acronym{IP} address of the @acronym{SMTP} client that is emitting the mail.
Usually it is @code{$client_addr}.
@item domain
@@ -6443,7 +6730,7 @@ traffic.
@table @var
@item ip
- The IP address of the @acronym{SMTP} client that is emitting the mail.
+ The @acronym{IP} address of the @acronym{SMTP} client that is emitting the mail.
Usually it is @code{$client_addr}.
@item domain
@@ -7391,6 +7678,7 @@ reject 503 5.0.0 "Need HELO command"
@end group
@end smallexample
+@anchor{header manipulation}
@cindex Header manipulation actions
Header manipulation actions allow you to add, delete or modify
message @acronym{RFC} 2822 headers.
@@ -7784,8 +8072,8 @@ argument @var{ipstr}, and checks its validity using the following algorithm:
Perform a @acronym{DNS} reverse-mapping for @var{ipstr}, looking up the
corresponding @code{PTR} record in @samp{in-addr.arpa}. For each record
-returned, look up its IP addresses (A records). If @var{ipstr} is
-among the returned IP addresses, return 1 (@code{true}), otherwise
+returned, look up its @acronym{IP} addresses (A records). If @var{ipstr} is
+among the returned @acronym{IP} addresses, return 1 (@code{true}), otherwise
return 0 (@code{false}).
The implementation of this function in @acronym{MFL} is:
@@ -7839,7 +8127,7 @@ System, it may fail to get a definite result. For example, the @acronym{DNS}
server can be down or temporary unavailable. In other words,
@code{hasmx} can be in a situation when, instead of returning
@samp{yes} or @samp{no}, it has to return @samp{don't know}. That is
-exactly when it will signal an @dfn{exception}.
+exactly then when it signals an @dfn{exception}.
@anchor{status.mfh}
@cindex exception types
@@ -7871,13 +8159,13 @@ This exception can be signaled by any @acronym{DNS}-related function
@cindex invcidr, exception type
@item invcidr
- Invalid CIDR notation. This is signaled by @code{match_cidr} function
-when its second argument is not a valid CIDR.
+ Invalid @acronym{CIDR} notation. This is signaled by @code{match_cidr} function
+when its second argument is not a valid @acronym{CIDR}.
@cindex invip, exception type
@item invip
- Invalid IP address. This is signaled by @code{match_cidr} function
-when its first argument is not a valid IP address.
+ Invalid @acronym{IP} address. This is signaled by @code{match_cidr} function
+when its first argument is not a valid @acronym{IP} address.
@cindex invtime, exception type
@item invtime
@@ -7898,7 +8186,7 @@ exception.
@cindex noresolve, exception type
@item noresolve
The argument of a @acronym{DNS}-related function cannot be resolved to host
-name or IP address. Currently only @code{ismx} (@pxref{ismx}) raises
+name or @acronym{IP} address. Currently only @code{ismx} (@pxref{ismx}) raises
this exception.
@cindex range, exception type
@@ -8136,7 +8424,7 @@ throw @var{excode} @var{descr}
@code{catch} statement: @var{excode} gives the numeric code of the
exception, @var{descr} gives its textual description. This statement
can be used in complex scripts to create non-local exits from deeply
-nested statements. @FIXME{(Elaborate on that).}
+nested statements. @FIXME{Elaborate on that.}
Notice several limitations of @code{throw}: first, the @var{excode}
argument must be an immediate value, you cannot use an expression in
@@ -8470,7 +8758,7 @@ do
@end group
@end smallexample
- Next rule rejects all messages coming from hosts with dynamic IP
+ Next rule rejects all messages coming from hosts with dynamic @acronym{IP}
addresses. A regular expression used to catch such hosts is not 100%
fail-proof, but it tries to cover most existing host naming patterns:
@@ -8484,7 +8772,7 @@ ppp|dhcp|dynamic|[-.]cpe[-.]).*"
@end smallexample
Messages coming from the machines whose host name contains
-something similar to an IP are subject to strict checking:
+something similar to an @acronym{IP} are subject to strict checking:
@smallexample
@group
@@ -8590,6 +8878,7 @@ words:
@item accept
@item add
@item and
+@item begin
@item break
@item case
@item catch
@@ -8600,7 +8889,8 @@ words:
@item discard
@item do
@item done
-@item echo
+@item echo
+@item end
@item elif
@item else
@item fi
@@ -8988,7 +9278,7 @@ values}, for the detailed discussion of this option.
@table @option
@opsummary{milter-timeout}
@item --milter-timeout=@var{interval}
-Set MTA connection timeout. Overrides @samp{#pragma option
+Set @acronym{MTA} connection timeout. Overrides @samp{#pragma option
milter-timeout}, which you are advised to use instead (@pxref{pragma
milter-timeout}.
@@ -9061,12 +9351,50 @@ Overrides @code{#pragma option stack-trace}, which you are advised to
use instead (@pxref{pragma stack-trace}). @xref{tracing runtime
errors}, for the detailed description of this feature.
-@opsummary{gacopyz-debug}
-@item --gacopyz-debug
-Instruct @command{gacopyz} library to produce a trace of its
-interaction with the @acronym{MTA}. This option is meant to assist in
-developing @command{gacopyz}. It issues huge amounts of information
-to log files.
+@opsummary{gacopyz-log}
+@item --gacopyz-log=@var{level}
+Set desired logging level for @command{gacopyz} library
+(@pxref{Gacopyz}). There are five logging levels. The following
+table lists them in order of decreasing priority:
+
+@table @asis
+@item fatal
+Log fatal errors.
+
+@item err
+Log error messages.
+
+@item warn
+Log warning messages.
+
+@item info
+Log informational messages. In particular, this enables printing
+messages on each subprocess startup and termination, which look like
+that:
+
+@smallexample
+Apr 28 09:00:11 host mailfromd[9411]: connect from 192.168.10.1:50398
+Apr 28 09:00:11 host mailfromd[9411]: finishing connection
+@end smallexample
+
+This level can be useful for debugging your scripts.
+
+@item debug
+Log debugging information. This level prints huge amounts of
+information, in particular it displays dumps of each Milter packet
+sent and received.
+
+@end table
+
+Although it is possible to set these levels independently of each
+other, it is seldom practical. Therefore, the option
+@option{--gacopyz-log=@var{level}} enables all logging levels from
+@var{level} up. For example, @option{--gacopyz-log=warn} enables
+log levels @samp{warn}, @samp{err} and @samp{fatal}. It is the
+default. If you need to trace each subprocess startup and shutdown,
+set @option{--gacopyz-log=info}. Setting the logging level to
+@samp{debug} can be needed only for @command{Gacopyz} developers, to
+debug the protocol.
@xref{Testing Filter Scripts}.
diff --git a/doc/mtasim.texi b/doc/mtasim.texi
index fb4155a..f86a89d 100644
--- a/doc/mtasim.texi
+++ b/doc/mtasim.texi
@@ -207,7 +207,7 @@ therefore it must appear @emph{before} @samp{--})
@end smallexample
@cindex @command{mtasim} administrative commands
- While the @acronym{SMTP} do not need any clarification, some words
+ While the @acronym{SMTP} commands do not need any clarification, some words
about the @dfn{administrative commands} are surely in place. These
commands allow to define, undefine and list arbitrary Sendmail macros.
Each administrative command consists of a backslash followed by a command
@@ -354,7 +354,7 @@ quit
@end smallexample
@cindex @command{mtasim}, using in shell scripts
- This sample illustrates also the fact that you can use
+ This example also illustrates the fact that you can use
@samp{#}-style comments in the @command{mtasim} input. Such a script
can be used in shell programs, for example:
@@ -371,7 +371,7 @@ fi
@section Trace Files
@cindex trace file, @command{mtasim}
- It is possible to log the entire @acronym{SMTP} session to a file.
+ It is possible to log an entire @acronym{SMTP} session to a file.
This is called @dfn{session tracing}. Two options are provided for
this purpose:
@@ -400,7 +400,7 @@ using the following option:
is not quite the same as Sendmail @option{-bd} mode. When started in
@dfn{daemon} mode, @command{mtasim} selects the first available
@acronym{TCP} port to use from the range @samp{1024 -- 65535}.
-It prints the port number it selected on standard output and
+It prints the selected port number on the standard output and
starts listening on it. When a connection comes, it serves a
@emph{single} @acronym{SMTP} session and exits immediately when it is
ended.
diff --git a/gacopyz/gacopyz.h b/gacopyz/gacopyz.h
index 578f25d..0e40e17 100644
--- a/gacopyz/gacopyz.h
+++ b/gacopyz/gacopyz.h
@@ -127,7 +127,9 @@ extern "C" {
#define SMI_LOG_FATAL 4
#define SMI_LOG_MASK(n) (1<<(n))
-
+#define SMI_LOG_UPTO(n) ((1 << ((n)+1))-1) /* all levels through n */
+#define SMI_LOG_FROM(n) (SMI_LOG_UPTO(SMI_LOG_FATAL) & ~SMI_LOG_UPTO(n-1))
+
#define SMI_DEFAULT_LOG_MASK SMI_LOG_MASK(SMI_LOG_INFO) \
| SMI_LOG_MASK(SMI_LOG_WARN) \
| SMI_LOG_MASK(SMI_LOG_ERR) \
@@ -310,6 +312,9 @@ void gacopyz_set_logger(void (*)(int, char *, va_list));
void gacopyz_stderr_log_printer(int level, char *fmt, va_list ap);
void gacopyz_syslog_log_printer(int level, char *fmt, va_list ap);
+int gacopyz_string_to_log_level(const char *str);
+const char *gacopyz_log_level_to_string(int level);
+
/* Server */
typedef struct gacopyz_srv *gacopyz_srv_t;
diff --git a/gacopyz/log.c b/gacopyz/log.c
index 0514456..b1dd234 100644
--- a/gacopyz/log.c
+++ b/gacopyz/log.c
@@ -54,10 +54,28 @@ static char *level_name[] = {
"FATAL"
};
+int
+gacopyz_string_to_log_level(const char *str)
+{
+ int i;
+ for (i = 0; i < sizeof level_name / sizeof level_name[0]; i++)
+ if (strcasecmp (str, level_name[i]) == 0)
+ return i;
+ return -1;
+}
+
+const char *
+gacopyz_log_level_to_string(int level)
+{
+ if (level < 0 || level > sizeof level_name / sizeof level_name[0])
+ return NULL;
+ return level_name[level];
+}
+
void
gacopyz_stderr_log_printer(int level, char *fmt, va_list ap)
{
- fprintf(stderr, "Gacopyz %s: ", level_name[level]);
+ fprintf(stderr, "Gacopyz %s: ", gacopyz_log_level_to_string(level));
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
}
diff --git a/gacopyz/smfi.c b/gacopyz/smfi.c
index 7288943..60fb843 100644
--- a/gacopyz/smfi.c
+++ b/gacopyz/smfi.c
@@ -36,18 +36,8 @@ smfi_setdbg(int level)
int
smfi_setlogmask(int logmask)
{
- switch (logmask) {
- case SMI_LOG_DEBUG:
- case SMI_LOG_INFO:
- case SMI_LOG_WARN:
- case SMI_LOG_ERR:
- case SMI_LOG_FATAL:
- __smfi_logmask = SMI_LOG_MASK(logmask);
- return MI_SUCCESS;
-
- default:
- return MI_FAILURE;
- }
+ __smfi_logmask = logmask;
+ return MI_SUCCESS;
}
int
diff --git a/src/bi_db.m4 b/src/bi_db.m4
index 84b2bb9..9fa94f0 100644
--- a/src/bi_db.m4
+++ b/src/bi_db.m4
@@ -168,6 +168,7 @@ greylist_expire_item(const void *content)
static struct db_format greylist_format_struct = {
"greylist",
DEFAULT_GREYLIST_DATABASE,
+ 1,
DEFAULT_EXPIRE_INTERVAL,
greylist_print_item,
greylist_expire_item
@@ -191,9 +192,6 @@ MF_DEFUN(greylist, NUMBER, STRING email, NUMBER interval)
int readonly;
time_t now;
- if (prog_trace_option)
- prog_trace(env, "GREYLIST \"%s\" %s %ld %ld", greylist_format->dbname, email, interval);
-
rc = mu_dbm_open(greylist_format->dbname, &db, MU_STREAM_RDWR, 0600,
&readonly);
MF_ASSERT(rc == 0, mf_dbfailure, "mu_dbm_open(%s) failed: %s",
@@ -288,6 +286,26 @@ MF_DEFUN(db_name, STRING, STRING fmtid)
}
END
+MF_DEFUN(db_get_active, NUMBER, STRING fmtid)
+{
+ struct db_format *fmt = db_format_lookup(fmtid);
+ MF_ASSERT(fmt != NULL,
+ mf_not_found,
+ "no such db format: %s", fmtid);
+ MF_RETURN(fmt->enabled);
+}
+END
+
+MF_DEFUN(db_set_active, VOID, STRING fmtid, NUMBER active)
+{
+ struct db_format *fmt = db_format_lookup(fmtid);
+ MF_ASSERT(fmt != NULL,
+ mf_not_found,
+ "no such db format: %s", fmtid);
+ fmt->enabled = active;
+}
+END
+
MF_DEFUN(db_expire_interval, NUMBER, STRING fmtid)
{
struct db_format *fmt = db_format_lookup(fmtid);
diff --git a/src/bi_io.m4 b/src/bi_io.m4
index 1d132f2..d443997 100644
--- a/src/bi_io.m4
+++ b/src/bi_io.m4
@@ -164,7 +164,7 @@ open_program_stream(struct io_stream *str, const char *cmdline, int flags)
/* Fork has failed */
/* Restore things */
rc = errno;
- if (REDIRECT_STDOUT_P (flags)) {
+ if (REDIRECT_STDOUT_P(flags)) {
close(rightp[0]);
close(rightp[1]);
}
diff --git a/src/cache.c b/src/cache.c
index 30c9551..ba2ca74 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -42,6 +42,9 @@ cache_get(char *email)
DBM_DATUM contents;
int readonly = 0;
int res;
+
+ if (!cache_format->enabled)
+ return mf_failure;
debug1(50, "getting cache info for %s", email);
if (mu_dbm_open(cache_format->dbname, &db, MU_STREAM_RDWR, 0600,
@@ -101,6 +104,9 @@ cache_insert(char *email, mf_status rc)
struct cache_result res;
char timebuf[80];
+ if (!cache_format->enabled)
+ return;
+
time(&res.timestamp);
res.status = rc;
debug4(50,"inserting cache info for %s. status=%s (%d), time=%s",
@@ -149,7 +155,11 @@ mf_status
cache_get2(char *email, char *client_addr)
{
mf_status rc = mf_failure;
- size_t size = strlen(email) + 1 + strlen(client_addr) + 1;
+ size_t size;
+
+ if (!cache_format->enabled)
+ return mf_failure;
+ size = strlen(email) + 1 + strlen(client_addr) + 1;
char *key = malloc(size);
if (key) {
strcat(strcat(strcpy(key, email), ":"), client_addr);
@@ -162,18 +172,23 @@ cache_get2(char *email, char *client_addr)
void
cache_insert2(char *email, char *client_addr, mf_status rc)
{
- size_t size = strlen(email) + 1 + strlen(client_addr) + 1;
- char *key = malloc(size);
- if (key) {
- strcat(strcat(strcpy(key, email), ":"), client_addr);
- cache_insert(key, rc);
- free(key);
+ if (!cache_format->enabled)
+ return;
+ else {
+ size_t size = strlen(email) + 1 + strlen(client_addr) + 1;
+ char *key = malloc(size);
+ if (key) {
+ strcat(strcat(strcpy(key, email), ":"), client_addr);
+ cache_insert(key, rc);
+ free(key);
+ }
}
}
static struct db_format cache_format_struct = {
"cache",
DEFAULT_DATABASE,
+ 1,
DEFAULT_EXPIRE_INTERVAL,
cache_print_item,
cache_expire_item
diff --git a/src/dnscache.c b/src/dnscache.c
index 44d8e28..57608e8 100644
--- a/src/dnscache.c
+++ b/src/dnscache.c
@@ -111,6 +111,8 @@ dns_cache_get(int type, const char *keystr, char **rbuf, size_t rcnt)
DBM_DATUM contents;
int res;
+ if (!dns_cache_format->enabled)
+ return mf_failure;
if (dns_make_key(type, keystr, &key))
return mf_failure;
debug1(50, "%s: looking up in cache", MU_DATUM_PTR(key));
@@ -165,6 +167,8 @@ dns_cache_put(int type, const char *keystr, time_t ttl,
size_t len;
int res;
+ if (!dns_cache_format->enabled)
+ return;
if (dns_make_key(type, keystr, &key))
return;
if (mu_dbm_open(dns_cache_format->dbname, &db, MU_STREAM_RDWR,
@@ -238,6 +242,7 @@ dns_expire_item(const void *content)
static struct db_format dns_cache_format_struct = {
"dns",
DEFAULT_DNS_DATABASE,
+ 1,
DEFAULT_DNS_NEGATIVE_EXPIRE_INTERVAL,
dns_print_item,
dns_expire_item
diff --git a/src/engine.c b/src/engine.c
index ca82fee..3e41869 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -90,6 +90,8 @@ priv_get(SMFICTX *ctx)
test_data = md;
else
gacopyz_setpriv(ctx, md);
+ env_init(md->env);
+ xeval(md->env, smtp_state_begin);
}
}
if (!md->msgid[0]) {
@@ -867,6 +869,8 @@ filter_cleanup(SMFICTX *ctx)
struct message_data *md = priv_get(ctx);
debug(100,"cleaning up");
if (md) {
+ env_init(md->env);
+ xeval(md->env, smtp_state_end);
free(md->helostr);
destroy_environment(md->env);
smtp_io_data_destroy(md->io);
@@ -887,6 +891,9 @@ xeval(eval_environ_t env, enum smtp_state tag)
rc = eval_environment(env, entry_point[tag]);
if (rc)
mu_error("Execution of the configuration program was not finished");
+ if (tag == smtp_state_begin)
+ env_save_catches(env);
+
return rc;
}
@@ -1146,7 +1153,12 @@ mlfi_eom(SMFICTX *ctx)
sfsistat
mlfi_abort(SMFICTX *ctx)
{
+ struct message_data *md = priv_get(ctx);
+
debug(70, "Abort");
+ mu_list_destroy(&md->hdr);
+ md->hdr = NULL;
+ md->msgid[0] = 0;
return SMFIS_CONTINUE;
}
diff --git a/src/gram.y b/src/gram.y
index 02e5219..a4d6e28 100644
--- a/src/gram.y
+++ b/src/gram.y
@@ -64,11 +64,14 @@ static NODE *create_asgn_node(struct variable *var, NODE *expr,
static void register_auto(struct variable *var);
static void unregister_auto(struct variable *var);
-static size_t forget_autos(size_t nparam);
+static size_t forget_autos(size_t nparam, size_t prev);
static void optimize(NODE *node);
static NODE *root_node[smtp_state_count];
prog_counter_t entry_point[smtp_state_count];
+
+#define PS_BEGIN 0
+#define PS_END 1
int regex_flags; /* Should default to REG_NOSUB ? */
unsigned error_count; /* Number of detected errors */
@@ -92,7 +95,7 @@ size_t catch_nesting; /* Nesting level for catch statements */
within a `when .*:' construct. */
static int allow_unquoted_strings;
-static struct stmtlist genstmt; /* List of generated statements */
+static struct stmtlist genstmt; /* List of generated statements */
/* State handlers and their positional parameters */
struct state_parms {
@@ -100,6 +103,7 @@ struct state_parms {
data_type_t types[4]; /* Their data types */
} state_parms[] = {
{ 0, }, /* smtp_state_none */
+ { 0, }, /* smtp_state_begin */
{ 4, { dtype_string, dtype_number, dtype_number, dtype_string } },
/* smtp_state_connect */
{ 1, { dtype_string } }, /* smtp_state_helo */
@@ -110,6 +114,7 @@ struct state_parms {
{ 0, }, /* smtp_state_eoh */
{ 2, { dtype_string, dtype_number } }, /* smtp_state_body */
{ 0, }, /* smtp_state_eom */
+ { 0, } /* smtp_state_end */
};
struct parmtype {
@@ -178,7 +183,7 @@ struct parminfo {
{ parmcount_none, parmtype_none },
{ parmcount_handler, parmtype_handler },
{ parmcount_catch, parmtype_catch },
- { parmcount_function, parmtype_function }
+ { parmcount_function, parmtype_function },
};
#define PARMCOUNT() parminfo[inner_context].parmcount()
@@ -232,6 +237,9 @@ check_func_usage(struct function *fp)
case context_function:
func->statemask |= fp->statemask;
break;
+
+ default:
+ break;
}
return 0;
}
@@ -318,11 +326,15 @@ static void register_macro(enum smtp_state tag, const char *macro);
} case_list ;
struct case_stmt *case_stmt;
struct loop_node loop;
+ struct {
+ struct locus locus;
+ int code;
+ } progspecial;
};
%token <locus> ACT_ACCEPT ACT_REJECT ACT_TEMPFAIL ACT_CONTINUE ACT_DISCARD
%token <locus> ADD REPLACE DELETE
-%token <locus> PROG IF FI ELSE ELIF
+%token <locus> PROG KW_BEGIN KW_END IF FI ELSE ELIF
%token <locus> ON HOST FROM AS DO DONE POLL MATCHES FNMATCHES
%token <locus> MXMATCHES MXFNMATCHES
%token <locus> WHEN PASS SET CATCH THROW KW_ECHO RETURNS RETURN FUNC
@@ -350,7 +362,8 @@ static void register_macro(enum smtp_state tag, const char *macro);
%left UMINUS
%left BUILTIN FUNCTION FUNCTION_P BUILTIN_P
-%type <node> decl stmt condition action if_cond else_cond on_cond atom
+%type <node> decl stmt condition action sendmail_action header_action
+ if_cond else_cond on_cond atom
funcall proccall expr common_expr simp_expr atom_expr
asgn catch throw return case_cond autodcl constdecl
loopstmt opt_while jumpstmt
@@ -374,6 +387,7 @@ static void register_macro(enum smtp_state tag, const char *macro);
%type <case_list> cond_branches branches
%type <case_stmt> cond_branch branch
%type <locus> on
+%type <progspecial> progspecial
%%
@@ -421,16 +435,42 @@ decl : PROG state_ident DO stmtlist DONE
{
$$ = alloc_node(node_type_progdecl, &$1);
$$->v.progdecl.tag = $2;
- $$->v.progdecl.auto_count = forget_autos(PARMCOUNT());
+ $$->v.progdecl.auto_count = forget_autos(PARMCOUNT(), 0);
$$->v.progdecl.tree = $4.head;
outer_context = inner_context = context_none;
+ state_tag = smtp_state_none;
+ }
+ | progspecial DO stmtlist DONE
+ {
+ static NODE *progspecial[2];
+ NODE *np = progspecial[$1.code];
+
+ if (!np) {
+ np = alloc_node(node_type_progdecl, &$1.locus);
+ $$ = progspecial[$1.code] = np;
+ np->v.progdecl.tag = state_tag;
+ np->v.progdecl.tree = $3.head;
+ np->v.progdecl.auto_count = 0;
+ } else {
+ NODE *cur;
+ for (cur = np->v.progdecl.tree; cur->next;
+ cur = cur->next)
+ ;
+ cur->next = $3.head;
+ $$ = NULL;
+ }
+
+ np->v.progdecl.auto_count = forget_autos(PARMCOUNT(),
+ np->v.progdecl.auto_count);
+ outer_context = inner_context = context_none;
+ state_tag = smtp_state_none;
}
| FUNC fundecl DO stmtlist DONE
{
func->node = $4.head;
$$ = declare_function(func, &$1,
- forget_autos(PARMCOUNT()));
+ forget_autos(PARMCOUNT(), 0));
outer_context = inner_context = context_none;
func = NULL;
}
@@ -444,6 +484,22 @@ decl : PROG state_ident DO stmtlist DONE
}
;
+progspecial: KW_BEGIN
+ {
+ state_tag = smtp_state_begin;
+ outer_context = inner_context = context_handler;
+ $$.locus = $1;
+ $$.code = PS_BEGIN;
+ }
+ | KW_END
+ {
+ state_tag = smtp_state_end;
+ outer_context = inner_context = context_handler;
+ $$.locus = $1;
+ $$.code = PS_END;
+ }
+ ;
+
vardecl : TYPE IDENTIFIER
{
if (!vardecl($2->text, $1, storage_extern, NULL))
@@ -604,7 +660,8 @@ parm : TYPE IDENTIFIER
$$ = xmalloc(sizeof *$$);
$$->next = NULL;
if (($$->type = string_to_type($1->text)) == dtype_unspecified)
- parse_error("unknown type specification: %s", $1->text);
+ parse_error("unknown type specification: %s",
+ $1->text);
}
;
@@ -708,7 +765,37 @@ autodcl : TYPE IDENTIFIER
}
;
-action : ACT_ACCEPT maybe_triplet
+action : sendmail_action
+ {
+ if (inner_context == context_handler) {
+ if (state_tag == smtp_state_begin)
+ parse_error("sendmail action is not "
+ "allowed in a begin block");
+ else if (state_tag == smtp_state_end)
+ parse_error("sendmail action is not "
+ "allowed in an end block");
+ }
+ }
+ | header_action
+ {
+ if (inner_context == context_handler
+ && state_tag == smtp_state_end)
+ parse_error("header action is not allowed "
+ "in an end block");
+ }
+ | PASS
+ {
+ $$ = alloc_node(node_type_noop, &$1);
+ }
+ | KW_ECHO expr
+ {
+ $$ = alloc_node(node_type_echo, &$1);
+ $$->v.node = cast_to(dtype_string, $2);
+ }
+ ;
+
+sendmail_action:
+ ACT_ACCEPT maybe_triplet
{
if ($2.code || $2.xcode || $2.message)
parse_warning("arguments are ignored for accept");
@@ -748,11 +835,10 @@ action : ACT_ACCEPT maybe_triplet
memset(&$$->v.ret, 0, sizeof $$->v.ret);
$$->v.ret.stat = SMFIS_DISCARD;
}
- | PASS
- {
- $$ = alloc_node(node_type_noop, &$1);
- }
- | ADD string expr
+ ;
+
+header_action:
+ ADD string expr
{
$$ = alloc_node(node_type_header, &$1);
$$->v.hdr.opcode = header_add;
@@ -772,12 +858,7 @@ action : ACT_ACCEPT maybe_triplet
$$->v.hdr.opcode = header_delete;
$$->v.hdr.name = $2;
}
- | KW_ECHO expr
- {
- $$ = alloc_node(node_type_echo, &$1);
- $$->v.node = cast_to(dtype_string, $2);
- }
- ;
+ ;
maybe_triplet: /* empty */
{
@@ -2726,6 +2807,8 @@ mailfromd_test(int argc, char **argv)
env = create_environment(NULL,
dict_getsym, dbg_setreply, dbg_setheader,
dict);
+ xeval(env, smtp_state_begin);
+
env_init(env);
for (i = 0; i < argc; i++) {
if (p = strchr(argv[i], '=')) {
@@ -2759,6 +2842,9 @@ mailfromd_test(int argc, char **argv)
rc = xeval(env, test_state);
env_leave_frame(env, state_parms[test_state].cnt);
env_final_gc(env);
+
+ xeval(env, smtp_state_end);
+
status = environment_get_status(env);
printf("State %s: ", state_to_string(test_state));
print_stat(status);
@@ -2911,6 +2997,7 @@ static struct tagtable {
enum smtp_state tag;
} tagtable[] = {
{ "none", smtp_state_none },
+ { "begin", smtp_state_begin },
{ "connect", smtp_state_connect },
{ "helo", smtp_state_helo },
{ "envfrom", smtp_state_envfrom },
@@ -2920,6 +3007,7 @@ static struct tagtable {
{ "eoh", smtp_state_eoh },
{ "body", smtp_state_body },
{ "eom", smtp_state_eom },
+ { "end", smtp_state_end },
};
enum smtp_state
@@ -3703,9 +3791,9 @@ unregister_auto(struct variable *var)
}
static size_t
-forget_autos(size_t nparam)
+forget_autos(size_t nparam, size_t prev)
{
- size_t auto_count = 0;
+ size_t auto_count = prev;
size_t param_count = 0;
struct variable *var = auto_list;
while (var) {
diff --git a/src/lex.l b/src/lex.l
index 2964633..017e835 100644
--- a/src/lex.l
+++ b/src/lex.l
@@ -539,6 +539,8 @@ while return keyword(WHILE);
for return keyword(FOR);
break return keyword(BREAK);
pass return keyword(PASS);
+begin return keyword(KW_BEGIN);
+end return keyword(KW_END);
{MACRO} { return builtin_macro(yytext); }
<ONBLOCK>poll return keyword(POLL);
diff --git a/src/mailfromd.h b/src/mailfromd.h
index ace58e8..61bd3ae 100644
--- a/src/mailfromd.h
+++ b/src/mailfromd.h
@@ -78,8 +78,9 @@ typedef enum mf_status_code {
/* SMTP (libmilter) states */
enum smtp_state {
smtp_state_none,
+ smtp_state_begin,
+ smtp_state_first=smtp_state_begin,
smtp_state_connect,
- smtp_state_first=smtp_state_connect,
smtp_state_helo,
smtp_state_envfrom,
smtp_state_envrcpt,
@@ -89,6 +90,8 @@ enum smtp_state {
smtp_state_body,
smtp_state_eom,
+ smtp_state_end,
+
smtp_state_count
};
@@ -782,6 +785,7 @@ typedef int (*db_expire_t)(const void *content);
struct db_format {
char *name;
char *dbname;
+ int enabled;
time_t expire_interval;
db_item_printer_t print_item;
db_expire_t expire;
diff --git a/src/main.c b/src/main.c
index 56aa2af..d389ec4 100644
--- a/src/main.c
+++ b/src/main.c
@@ -805,7 +805,7 @@ enum mailfromd_option {
OPTION_DUMP_XREF,
OPTION_EXPIRE,
OPTION_FOREGROUND,
- OPTION_DEBUG_GACOPYZ,
+ OPTION_GACOPYZ_LOG,
OPTION_IGNORE_FAILED_READS,
OPTION_LINT,
OPTION_LOG_TAG,
@@ -958,8 +958,8 @@ static struct argp_option options[] = {
{ "xref", OPTION_DUMP_XREF, NULL, 0,
N_("Produce a cross-reference listing"), GRP+1 },
{ "dump-xref", 0, NULL, OPTION_ALIAS, NULL, GRP+1 },
- { "gacopyz-debug", OPTION_DEBUG_GACOPYZ, NULL, 0,
- N_("Milter protocol trace"), GRP+1 },
+ { "gacopyz-log", OPTION_GACOPYZ_LOG, N_("LEVEL"), 0,
+ N_("Set Gacopyz log level"), GRP+1 },
{ "stderr", 's', NULL, 0,
N_("Log to stderr"), GRP+1 },
{ "syslog", OPTION_SYSLOG, NULL, 0,
@@ -1201,9 +1201,15 @@ parse_opt (int key, char *arg, struct argp_state *state)
foreground = 1;
break;
- case OPTION_DEBUG_GACOPYZ:
- smfi_setdbg(1);
+ case OPTION_GACOPYZ_LOG:
+ {
+ int lev = gacopyz_string_to_log_level(arg);
+ if (lev == -1)
+ argp_error(state, "%s: invalid Gacopyz log level",
+ arg);
+ smfi_setlogmask(SMI_LOG_FROM(lev));
break;
+ }
case OPTION_LIST:
need_config = 0;
@@ -1259,7 +1265,11 @@ parse_opt (int key, char *arg, struct argp_state *state)
case OPTION_TRACE_PROGRAM:
enable_prog_trace(arg ? arg: "all");
break;
-
+
+ case ARGP_KEY_INIT:
+ smfi_setlogmask(SMI_LOG_FROM(SMI_LOG_WARN));
+ break;
+
case ARGP_KEY_FINI:
if (validate_options())
exit(EX_USAGE);
diff --git a/src/prog.c b/src/prog.c
index a819b73..130a770 100644
--- a/src/prog.c
+++ b/src/prog.c
@@ -269,6 +269,7 @@ struct eval_environ {
mu_stream_t stream; /* Capture stream */
/* Non-local exits */
+ prog_counter_t defcatch[mf_status_count];
prog_counter_t catch[mf_status_count];
/* Built-in private data */
@@ -1734,8 +1735,7 @@ env_init(eval_environ_t env)
env->string = NULL;
/* Initialize catch functions */
- for (i = 0; i < mf_status_count; i++)
- env->catch[i] = 0;
+ memcpy (env->catch, env->defcatch, sizeof env->catch);
env_final_gc(env);
}
@@ -2104,6 +2104,8 @@ create_environment(SMFICTX *ctx,
env->toh = datasize;
env->bi_priv_array = NULL;
+
+ memset(env->defcatch, 0, sizeof env->defcatch);
return env;
}
@@ -2300,3 +2302,9 @@ env_final_gc(eval_environ_t env)
}
}
+void
+env_save_catches(eval_environ_t env)
+{
+ memcpy(env->defcatch, env->catch, sizeof env->defcatch);
+}
+
diff --git a/src/rate.c b/src/rate.c
index 5c0ab7c..624a16e 100644
--- a/src/rate.c
+++ b/src/rate.c
@@ -174,6 +174,7 @@ rate_expire_item(const void *content)
static struct db_format rate_format_struct = {
"rate",
DEFAULT_RATE_DATABASE,
+ 1,
DEFAULT_EXPIRE_INTERVAL,
rate_print_item,
rate_expire_item

Return to:

Send suggestions and report system problems to the System administrator.