summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org.ua>2009-05-11 18:48:14 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2009-05-11 18:48:14 (GMT)
commitf8323650568b0031f0ec4c50cde417cb10a48c56 (patch) (side-by-side diff)
treeb0007c30366860dc079a7bf8828d254b7486630f
parentffd2cf189d926abe00de0a79f292f1ea69d02aac (diff)
downloadmailfromd-f8323650568b0031f0ec4c50cde417cb10a48c56.tar.gz
mailfromd-f8323650568b0031f0ec4c50cde417cb10a48c56.tar.bz2
Implement functional notation for reply actions
* NEWS, doc/mailfromd.texi: Update. * mfd/drivers.c (print_type_result, mark_type_result) (optimize_type_result, code_type_result): Rewrite. * mfd/gram.y: Rewrite action rules. * mfd/lex.l (CODE,XCODE): Remove. * mfd/opcodes (RESULT): Takes one immediate parameter. (instr_result): Get arguments from stack.
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--NEWS19
-rw-r--r--doc/mailfromd.texi86
-rw-r--r--mfd/drivers.c115
-rw-r--r--mfd/gram.y131
-rw-r--r--mfd/lex.l4
-rw-r--r--mfd/mailfromd.h8
-rw-r--r--mfd/opcodes2
-rw-r--r--mfd/prog.c10
8 files changed, 276 insertions, 99 deletions
diff --git a/NEWS b/NEWS
index b50d87a..90b7a88 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-Mailfromd NEWS -- history of user-visible changes. 2009-05-10
+Mailfromd NEWS -- history of user-visible changes. 2009-05-11
Copyright (C) 2005, 2006, 2007, 2008, 2009 Sergey Poznyakoff
See the end of file for copying conditions.
@@ -24,6 +24,23 @@ is not strictly necessary. However, keep in mind that due to the
specifics of MeTA1, the number of symbols that may be exported for
each stage is limited (Mailfromd manual, section 11.1.2).
+* Reject and tempfail actions: Functional notation
+
+The reply actions `reject' and `tempfail' allow functional notation,
+i.e. their arguments can be supplied as to a function:
+
+ reject(550, 5.7.7, "IP address does not resolve")
+
+An important feature of this notation is that all three arguments are
+MFL expressions, which means that you can now compute the reply codes
+at run time:
+
+ reject(550 + %n, "5.7." %x, "Transaction rejected")
+
+An argument can be omitted, in which case the default value is used, e.g.:
+
+ reject(550 + %n, , "Transaction rejected")
+
* New functions
A set of new functions is added that allow to access the headers
diff --git a/doc/mailfromd.texi b/doc/mailfromd.texi
index 3d2e43c..090222e 100644
--- a/doc/mailfromd.texi
+++ b/doc/mailfromd.texi
@@ -1355,16 +1355,21 @@ execution of the program to stop and to return a response code to
the @command{Sendmail}. There are five actions, one for each response
code: @code{continue}, @code{accept}, @code{reject}, @code{discard},
and @code{tempfail}. Among these, @code{reject} and @code{discard}
-can optionally take one to three arguments. The first
-argument is a three-digit @acronym{RFC} 2821 reply code. It must begin with
-@samp{5} for @code{reject} and with @samp{4} for @code{tempfail}. If
-two arguments are supplied, the second argument must be either an
-@dfn{extended reply code} (@acronym{RFC} 1893/2034) or a textual string to be
+can optionally take one to three arguments. There are two ways of
+supplying the arguments.
+
+In the first form, called @dfn{literal} or @dfn{traditional} notation,
+the arguments are supplied as additional words after the action name,
+separated by whitespace. The first argument is a three-digit
+@acronym{RFC} 2821 reply code. It must begin with @samp{5} for
+@code{reject} and with @samp{4} for @code{tempfail}. If two arguments
+are supplied, the second argument must be either an @dfn{extended
+reply code} (@acronym{RFC} 1893/2034) or a textual string to be
returned along with the @acronym{SMTP} reply. Finally, if all three
arguments are supplied, then the second one must be an extended reply
code and the third one must supply the textual string. The following
examples illustrate all possible ways of using the @code{reject}
-statement:
+statement in literal notation:
@smallexample
@group
@@ -1379,6 +1384,25 @@ reject 503 5.0.0 "Need HELO command"
@noindent
Please note the quotes around the textual string.
+Another form for these action is called @dfn{functional} notation,
+because it resembles the function syntax. When used in this form, the
+action word is followed by a parenthesized group of exactly three
+arguments, separated by commas. The meaning and ordering of the
+argument is the same as in literal form. Any of three arguments may
+be absent, in which case it will be replaced by the default value. To
+illustrate this, here are the statements from the previous example,
+written in functional notation:
+
+@smallexample
+@group
+reject(,,)
+reject(503,,)
+reject(503, 5.0.0)
+reject(503,, "Need HELO command")
+reject(503, 5.0.0, "Need HELO command")
+@end group
+@end smallexample
+
@node Conditional Execution
@section Conditional Execution
@@ -10253,7 +10277,8 @@ program.
perform a certain action over the message being processed. There are
two kinds of actions: return actions and header manipulation actions.
- Return actions tell @command{Sendmail} to return given response code
+@subsubheading Reply Actions
+Reply actions tell @command{Sendmail} to return given response code
to the remote party. There are five such actions:
@table @code
@@ -10263,14 +10288,16 @@ to the remote party. There are five such actions:
Return an @code{accept} reply. The remote party will continue
transmitting its message.
-@item reject [@var{code}] [@var{excode}] [@var{message}]
+@item reject @var{code} @var{excode} @var{message-expr}
+@itemx reject (@var{code-expr}, @var{excode-expr}, @var{message-expr})
@cindex reject action, defined
@kwindex reject
Return a @code{reject} reply. The remote party will have to
cancel transmitting its message. The three arguments are optional,
their usage is described below.
-@item tempfail [@var{code}] [@var{excode}] [@var{message}]
+@item tempfail @var{code} @var{excode} @var{message}
+@itemx tempfail (@var{code-expr}, @var{excode-expr}, @var{message-expr})
@cindex tempfail action, defined
@kwindex tempfail
Return a @samp{temporary failure} reply. The remote party can retry
@@ -10292,7 +10319,12 @@ continue processing of the message.
@anchor{reject}
Two actions, @code{reject} and @code{tempfail} can take up to three
-optional parameters. The first argument is a three-digit
+optional parameters. There are two forms of supplying these
+parameters.
+
+In the first form, called @dfn{literal} or @dfn{traditional} notation,
+the arguments are supplied as additional words after the action name,
+and are separated by whitespace. The first argument is a three-digit
@acronym{RFC} 2821 reply code. It must begin with @samp{5} for
@code{reject} and with @samp{4} for @code{tempfail}. If two arguments
are supplied, the second argument must be either an @dfn{extended
@@ -10313,6 +10345,40 @@ reject 503 5.0.0 "Need HELO command"
@end group
@end smallexample
+The notion @dfn{textual string}, used above means either a literal
+string or an @acronym{MFL} expression that evaluates to string.
+However, both code and extended code must always be literal.
+
+The second form of supplying arguments is called @dfn{functional}
+notation, because it resembles the function syntax. When used in this
+form, the action word is followed by a parenthesized group of exactly
+three arguments, separated by commas. Each argument is a
+@acronym{MFL} expression. The meaning and ordering of the arguments is
+the same as in literal form. Any or all of these three arguments may
+be absent, in which case it will be replaced by the default value. To
+illustrate this, here are the statements from the previous example,
+written in functional notation:
+
+@smallexample
+@group
+reject(,,)
+reject(503,,)
+reject(503, 5.0.0)
+reject(503, "Need HELO command",)
+reject(503, 5.0.0, "Need HELO command")
+@end group
+@end smallexample
+
+ Notice that there is an important difference between the two
+notations. The functional notation allows to compute both reply codes
+at run time, e.g.:
+
+@smallexample
+ reject(500 + %dig2*10 + %dig3, "5." %edig2 "." %edig2)
+@end smallexample
+
+@subsubheading Header Actions
+
@anchor{header manipulation}
@cindex header manipulation actions
@cindex actions, header manipulation
diff --git a/mfd/drivers.c b/mfd/drivers.c
index d8dfb4b..ed8a288 100644
--- a/mfd/drivers.c
+++ b/mfd/drivers.c
@@ -889,62 +889,107 @@ code_type_un(NODE *node, struct locus **old_locus)
void
print_type_result(NODE *node, int level)
{
- if (node->v.ret.code) {
- const char *s = NULL;
- int expr = 0;
-
- print_level(level);
- if (node->v.ret.message) {
- if (node->v.ret.message->type == node_type_string)
- s = node->v.ret.message->v.literal->text;
- else {
- expr = 1;
- s = "(expression)";
- }
- }
- dbg_setreply(NULL,
- (char*) LITERAL_TEXT(node->v.ret.code),
- (char*) LITERAL_TEXT(node->v.ret.xcode),
- (char*) s);
- if (expr)
- print_node(node->v.ret.message, level+1);
- }
+ NODE *code, *xcode;
+
+ code = node->v.ret.code;
+ xcode = node->v.ret.xcode;
+
print_level(level);
+ printf("SET REPLY ");
print_stat(node->v.ret.stat);
printf("\n");
+ print_level(level);
+ printf("CODE:\n");
+ if (code)
+ print_node(code, level+1);
+ print_level(level);
+ printf("XCODE:\n");
+ if (xcode)
+ print_node(xcode, level+1);
+ print_level(level);
+ printf("MESSAGE:\n");
+ if (node->v.ret.message)
+ print_node(node->v.ret.message, level+1);
+ printf("\n");
}
void
mark_type_result(NODE *node)
{
- if (node->v.ret.code)
- node->v.ret.code->flags |= VAR_REFERENCED;
- if (node->v.ret.xcode)
- node->v.ret.xcode->flags |= VAR_REFERENCED;
+ mark(node->v.ret.code);
+ mark(node->v.ret.xcode);
mark(node->v.ret.message);
}
void
optimize_type_result(NODE *node)
{
+ optimize(node->v.ret.code);
+ optimize(node->v.ret.xcode);
optimize(node->v.ret.message);
}
-void
-code_type_result(NODE *node, struct locus **old_locus)
+static void
+code_result_arg(NODE *node)
{
- MARK_LOCUS();
- if (node->v.ret.message)
- code_node(node->v.ret.message);
+ if (node)
+ code_node(node);
else {
code_op(opcode_push);
code_immediate(NULL);
}
+}
+
+static NODE *
+result_argptr(NODE *arg)
+{
+ if (arg && arg->type == node_type_string
+ && arg->v.literal->text[0] == 0)
+ arg = NULL;
+ return arg;
+}
+
+void
+code_type_result(NODE *node, struct locus **old_locus)
+{
+ NODE *code, *xcode;
+
+ code = result_argptr(node->v.ret.code);
+ xcode = result_argptr(node->v.ret.xcode);
+
+ switch (node->v.ret.stat) {
+ case SMFIS_REJECT:
+ if (code && code->type == node_type_string
+ && code->v.literal->text[0] != '5')
+ parse_error_locus(&node->locus,
+ _("Reject code should be 5xx"));
+ if (xcode && xcode->type == node_type_string
+ && xcode->v.literal->text[0] != '5')
+ parse_error_locus(&node->locus,
+ _("Reject extended code should be 5.x.x"));
+ break;
+
+ case SMFIS_TEMPFAIL:
+ if (code && code->type == node_type_string
+ && code->v.literal->text[0] != '4')
+ parse_error_locus(&node->locus,
+ _("Tempfail code should be 4xx"));
+ if (xcode && xcode->type == node_type_string
+ && xcode->v.literal->text[0] != '4')
+ parse_error_locus(&node->locus,
+ _("Tempfail extended code should be 4.x.x"));
+ break;
+ default:
+ break;
+ }
+
+ code_result_arg(node->v.ret.message);
+ code_result_arg(xcode);
+ code_result_arg(code);
+
MARK_LOCUS();
code_op(opcode_result);
code_immediate((void*)node->v.ret.stat);
- code_immediate((void*)LITERAL_OFF(node->v.ret.code));
- code_immediate((void*)LITERAL_OFF(node->v.ret.xcode));
code_op(opcode_nil);
}
@@ -1482,13 +1527,11 @@ code_type_catch(NODE *node, struct locus **old_locus)
code_op(opcode_jmp);
jump_pc = code_immediate((void*)jump_pc);
} else {
- code_op(opcode_push);
- code_immediate(NULL);
+ code_result_arg(NULL);
+ code_result_arg(NULL);
+ code_result_arg(NULL);
code_op(opcode_result);
code_immediate(SMFIS_CONTINUE);
- code_immediate(NULL);
- code_immediate(NULL);
- code_immediate(NULL);
}
endpos = code_get_counter ();
diff --git a/mfd/gram.y b/mfd/gram.y
index 332a748..15ee960 100644
--- a/mfd/gram.y
+++ b/mfd/gram.y
@@ -299,7 +299,7 @@ _create_alias(void *item, void *data)
%}
%error-verbose
-%expect 29
+%expect 28
%union {
struct literal *literal;
@@ -360,7 +360,7 @@ _create_alias(void *item, void *data)
%token <locus> WHEN PASS SET CATCH THROW KW_ECHO RETURNS RETURN FUNC
%token <locus> SWITCH CASE DEFAULT CONST
%token <locus> FOR LOOP WHILE BREAK NEXT ARGCOUNT ALIAS DOTS ARGX VAPTR
-%token <literal> STRING CODE XCODE
+%token <literal> STRING
%token <literal> SYMBOL IDENTIFIER
%token <number> ARG NUMBER BACKREF
%token <builtin> BUILTIN BUILTIN_PROC BUILTIN_P
@@ -386,7 +386,8 @@ _create_alias(void *item, void *data)
%type <node> decl stmt condition action sendmail_action header_action
if_cond else_cond on_cond atom argref paren_argref
- funcall proccall expr common_expr simp_expr atom_expr
+ funcall proccall expr maybe_expr maybe_xcode_expr
+ common_expr simp_expr atom_expr
asgn catch throw return case_cond autodcl constdecl
loopstmt opt_while jumpstmt
%type <stmtlist> stmtlist decllist
@@ -394,10 +395,9 @@ _create_alias(void *item, void *data)
%type <poll> pollstmt pollarglist
%type <pollarg> pollarg loop_parm
%type <loop> opt_loop_parms loop_parm_list
-%type <number> number
%type <arglist> arglist
%type <var> variable
-%type <literal> string opt_ident loop_ident alias
+%type <literal> string opt_ident loop_ident alias code xcode
%type <state> state_ident
%type <matchtype> matches fnmatches
%type <type> retdecl
@@ -892,24 +892,32 @@ sendmail_action:
}
| ACT_REJECT maybe_triplet
{
- if ($2.code && $2.code->text[0] != '5')
- parse_error(_("Reject code should be 5xx"));
- if ($2.xcode && $2.xcode->text[0] != '5')
- parse_error(_("Reject extended code should be 5.x.x"));
$$ = alloc_node(node_type_result, &$1);
$$->v.ret = $2;
$$->v.ret.stat = SMFIS_REJECT;
}
+ | ACT_REJECT '(' maybe_expr ',' maybe_xcode_expr ',' maybe_expr ')'
+ {
+ $$ = alloc_node(node_type_result, &$1);
+ $$->v.ret.stat = SMFIS_REJECT;
+ $$->v.ret.code = $3 ? cast_to(dtype_string, $3) : NULL;
+ $$->v.ret.xcode = $5 ? cast_to(dtype_string, $5) : NULL;
+ $$->v.ret.message = $7 ? cast_to(dtype_string, $7) : NULL;
+ }
| ACT_TEMPFAIL maybe_triplet
{
- if ($2.code && $2.code->text[0] != '4')
- parse_error(_("Tempfail code should be 4xx"));
- if ($2.xcode && $2.xcode->text[0] != '4')
- parse_error(_("Tempfail extended code should be 4.x.x"));
$$ = alloc_node(node_type_result, &$1);
$$->v.ret = $2;
$$->v.ret.stat = SMFIS_TEMPFAIL;
}
+ | ACT_TEMPFAIL '(' maybe_expr ',' maybe_xcode_expr ',' maybe_expr ')'
+ {
+ $$ = alloc_node(node_type_result, &$1);
+ $$->v.ret.stat = SMFIS_TEMPFAIL;
+ $$->v.ret.code = $3 ? cast_to(dtype_string, $3) : NULL;
+ $$->v.ret.xcode = $5 ? cast_to(dtype_string, $5) : NULL;
+ $$->v.ret.message = $7 ? cast_to(dtype_string, $7) : NULL;
+ }
| ACT_CONTINUE
{
$$ = alloc_node(node_type_result, &$1);
@@ -924,6 +932,15 @@ sendmail_action:
}
;
+maybe_xcode_expr: maybe_expr
+ | xcode
+ {
+ $$ = alloc_node(node_type_string, get_locus());
+ $$->v.literal = $1;
+ }
+ ;
+
+
header_action:
ADD string expr
{
@@ -955,32 +972,74 @@ maybe_triplet: /* empty */
| triplet
;
-triplet : CODE
+triplet : code
{
- $$.code = $1;
+ $$.code = alloc_node(node_type_string, get_locus());
+ $$.code->v.literal = $1;
$$.xcode = NULL;
$$.message = NULL;
}
- | CODE XCODE
+ | code xcode
{
- $$.code = $1;
- $$.xcode = $2;
+ $$.code = alloc_node(node_type_string, get_locus());
+ $$.code->v.literal = $1;
+ $$.xcode = alloc_node(node_type_string, get_locus());
+ $$.xcode->v.literal = $2;
$$.message = NULL;
}
- | CODE XCODE expr
+ | code xcode expr
{
- $$.code = $1;
- $$.xcode = $2;
+ $$.code = alloc_node(node_type_string, get_locus());
+ $$.code->v.literal = $1;
+ $$.xcode = alloc_node(node_type_string, get_locus());
+ $$.xcode->v.literal = $2;
$$.message = cast_to(dtype_string, $3);
}
- | CODE expr
+ | code expr
{
- $$.code = $1;
+ $$.code = alloc_node(node_type_string, get_locus());
+ $$.code->v.literal = $1;
$$.xcode = NULL;
$$.message = cast_to(dtype_string, $2);
}
;
+code : NUMBER
+ {
+ char buf[4];
+
+ if ($1 < 200 || $1 > 599) {
+ yyerror(_("Invalid SMTP reply code"));
+ buf[0] = 0;
+ } else
+ snprintf(buf, sizeof(buf), "%lu", $1);
+ $$ = string_alloc(buf, strlen(buf));
+ }
+ ;
+
+xcode : NUMBER '.' NUMBER '.' NUMBER
+ {
+ char buf[sizeof("5.999.999")];
+
+ /* RFC 1893:
+ The syntax of the new status codes is defined as:
+
+ status-code = class "." subject "." detail
+ class = "2"/"4"/"5"
+ subject = 1*3digit
+ detail = 1*3digit
+ */
+ if (($1 != 2 && $1 != 4 && $1 !=5)
+ || $3 > 999 || $5 > 999) {
+ yyerror(_("Invalid extended reply code"));
+ buf[0] = 0;
+ } else
+ snprintf(buf, sizeof(buf), "%lu.%lu.%lu",
+ $1, $3, $5);
+ $$ = string_alloc(buf, strlen(buf));
+ }
+ ;
+
condition : if_cond
| case_cond
| on_cond
@@ -1121,7 +1180,7 @@ value : STRING
$$.v.literal = $1;
}
}
- | number
+ | NUMBER
{
$$.type = dtype_number;
$$.v.number = $1;
@@ -1164,19 +1223,6 @@ fnmatches : FNMATCHES
}
;
-number : NUMBER
- | CODE
- {
- char *p;
- $$ = strtol($1->text, &p, 10);
- if (*p) {
- /* should not happen */
- parse_error(_("Invalid number (near `%s')"), p);
- YYERROR;
- }
- }
- ;
-
/* Loop statements */
@@ -1402,6 +1448,13 @@ expr : NOT expr
| common_expr
;
+maybe_expr : /* empty */
+ {
+ $$ = NULL;
+ }
+ | expr
+ ;
+
common_expr: simp_expr
| common_expr simp_expr %prec CONCAT
{
@@ -1950,13 +2003,13 @@ on_cond : on pollstmt do branches DONE
on : ON
{
- onblock(1);
+ tie_in_onblock(1);
}
;
do : DO
{
- onblock(0);
+ tie_in_onblock(0);
}
;
diff --git a/mfd/lex.l b/mfd/lex.l
index 3ceabfc..c1f9c3e 100644
--- a/mfd/lex.l
+++ b/mfd/lex.l
@@ -318,8 +318,6 @@ vaptr return keyword(VAPTR);
yylval.number = strtoul(yytext+1, NULL, 0);
return BACKREF; }
/* Numeric strings */
-{N}\.{N}\.{N} { string(yytext, yyleng); return XCODE; }
-[0-9]{3} { string(yytext, yyleng); return CODE; }
0[xX]{X}{X}* { yylval.number = strtoul(yytext, NULL, 16); return NUMBER; };
0{O}{O}* { yylval.number = strtoul(yytext, NULL, 8); return NUMBER; };
0|{P} { yylval.number = strtoul(yytext, NULL, 10); return NUMBER; };
@@ -791,7 +789,7 @@ isemptystr(char *text)
}
void
-onblock(int enable)
+tie_in_onblock(int enable)
{
if (enable)
BEGIN(ONBLOCK);
diff --git a/mfd/mailfromd.h b/mfd/mailfromd.h
index ac93cba..bdf09dc 100644
--- a/mfd/mailfromd.h
+++ b/mfd/mailfromd.h
@@ -307,9 +307,9 @@ struct un_node { /* A unary operation node */
/* Return action node: accept/reject/tempfail/continue */
struct return_node {
sfsistat stat; /* Return status */
- struct literal *code; /* Code */
- struct literal *xcode; /* Extended code */
- NODE *message; /* Subtree producing the textual message */
+ NODE *code; /* Code */
+ NODE *xcode; /* Extended code */
+ NODE *message; /* Textual message */
};
enum msgmod_opcode { /* Message modification operation */
@@ -697,7 +697,7 @@ int yyparse();
int yylex();
int yyerror(char *s);
void add_include_dir(const char *dir);
-void onblock(int enable);
+void tie_in_onblock(int enable);
int parse_program(char *name, int ydebug);
void parse_pragma(const char *text);
const struct locus *get_locus(void);
diff --git a/mfd/opcodes b/mfd/opcodes
index cee68d4..dd883ea 100644
--- a/mfd/opcodes
+++ b/mfd/opcodes
@@ -90,5 +90,5 @@ RETCATCH NULL 0
FUNCALL dump_funcall 2
NEXT NULL 0
-RESULT dump_result 3
+RESULT dump_result 1
HEADER dump_header 2
diff --git a/mfd/prog.c b/mfd/prog.c
index 90a426e..f8e58a9 100644
--- a/mfd/prog.c
+++ b/mfd/prog.c
@@ -1487,9 +1487,9 @@ instr_result(eval_environ_t env)
char *code, *xcode;
char *message;
- get_string_arg(env, 0, &message);
- get_literal(env, 1, (const char**)&code);
- get_literal(env, 2, (const char**)&xcode);
+ get_string_arg(env, 2, &message);
+ get_string_arg(env, 1, &xcode);
+ get_string_arg(env, 0, &code);
if (PROG_TRACE_ENGINE)
prog_trace(env, "RESULT %d %s %s %s",
@@ -1513,8 +1513,8 @@ instr_result(eval_environ_t env)
env->status = status;
env->setreply(env->data, code, xcode, message);
- advance_pc(env, 3);
- adjust_stack(env, 1);
+ advance_pc(env, 1);
+ adjust_stack(env, 3);
}
void

Return to:

Send suggestions and report system problems to the System administrator.