aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2009-05-11 21:48:14 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2009-05-11 21:48:14 +0300
commitf8323650568b0031f0ec4c50cde417cb10a48c56 (patch)
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.
-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 b50d87a8..90b7a889 100644
--- a/NEWS
+++ b/NEWS
@@ -1,2 +1,2 @@
-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
@@ -26,2 +26,19 @@ 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
diff --git a/doc/mailfromd.texi b/doc/mailfromd.texi
index 3d2e43c5..090222eb 100644
--- a/doc/mailfromd.texi
+++ b/doc/mailfromd.texi
@@ -1357,7 +1357,12 @@ 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
@@ -1366,3 +1371,3 @@ 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:
@@ -1381,2 +1386,21 @@ 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
@@ -10255,3 +10279,4 @@ 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:
@@ -10265,3 +10290,4 @@ 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
@@ -10272,3 +10298,4 @@ 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
@@ -10294,3 +10321,8 @@ continue processing of the message.
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
@@ -10315,2 +10347,36 @@ reject 503 5.0.0 "Need HELO command"
+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}
diff --git a/mfd/drivers.c b/mfd/drivers.c
index d8dfb4b2..ed8a288a 100644
--- a/mfd/drivers.c
+++ b/mfd/drivers.c
@@ -891,25 +891,24 @@ 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");
}
@@ -919,6 +918,4 @@ 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);
@@ -929,2 +926,4 @@ optimize_type_result(NODE *node)
{
+ optimize(node->v.ret.code);
+ optimize(node->v.ret.xcode);
optimize(node->v.ret.message);
@@ -932,8 +931,7 @@ optimize_type_result(NODE *node)
-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 {
@@ -942,2 +940,51 @@ code_type_result(NODE *node, struct locus **old_locus)
}
+}
+
+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();
@@ -945,4 +992,2 @@ code_type_result(NODE *node, struct locus **old_locus)
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);
@@ -1484,9 +1529,7 @@ code_type_catch(NODE *node, struct locus **old_locus)
} 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);
}
diff --git a/mfd/gram.y b/mfd/gram.y
index 332a748a..15ee9602 100644
--- a/mfd/gram.y
+++ b/mfd/gram.y
@@ -301,3 +301,3 @@ _create_alias(void *item, void *data)
%error-verbose
-%expect 29
+%expect 28
@@ -362,3 +362,3 @@ _create_alias(void *item, void *data)
%token <locus> FOR LOOP WHILE BREAK NEXT ARGCOUNT ALIAS DOTS ARGX VAPTR
-%token <literal> STRING CODE XCODE
+%token <literal> STRING
%token <literal> SYMBOL IDENTIFIER
@@ -388,3 +388,4 @@ _create_alias(void *item, void *data)
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
@@ -396,6 +397,5 @@ _create_alias(void *item, void *data)
%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
@@ -894,6 +894,2 @@ sendmail_action:
{
- 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);
@@ -902,8 +898,12 @@ sendmail_action:
}
+ | 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);
@@ -912,2 +912,10 @@ sendmail_action:
}
+ | 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
@@ -926,2 +934,11 @@ sendmail_action:
+maybe_xcode_expr: maybe_expr
+ | xcode
+ {
+ $$ = alloc_node(node_type_string, get_locus());
+ $$->v.literal = $1;
+ }
+ ;
+
+
header_action:
@@ -957,5 +974,6 @@ maybe_triplet: /* empty */
-triplet : CODE
+triplet : code
{
- $$.code = $1;
+ $$.code = alloc_node(node_type_string, get_locus());
+ $$.code->v.literal = $1;
$$.xcode = NULL;
@@ -963,17 +981,22 @@ triplet : CODE
}
- | 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;
@@ -983,2 +1006,38 @@ triplet : CODE
+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
@@ -1123,3 +1182,3 @@ value : STRING
}
- | number
+ | NUMBER
{
@@ -1166,15 +1225,2 @@ 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;
- }
- }
- ;
-
@@ -1404,2 +1450,9 @@ expr : NOT expr
+maybe_expr : /* empty */
+ {
+ $$ = NULL;
+ }
+ | expr
+ ;
+
common_expr: simp_expr
@@ -1952,3 +2005,3 @@ on : ON
{
- onblock(1);
+ tie_in_onblock(1);
}
@@ -1958,3 +2011,3 @@ do : DO
{
- onblock(0);
+ tie_in_onblock(0);
}
diff --git a/mfd/lex.l b/mfd/lex.l
index 3ceabfce..c1f9c3e8 100644
--- a/mfd/lex.l
+++ b/mfd/lex.l
@@ -320,4 +320,2 @@ vaptr return keyword(VAPTR);
/* 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; };
@@ -793,3 +791,3 @@ isemptystr(char *text)
void
-onblock(int enable)
+tie_in_onblock(int enable)
{
diff --git a/mfd/mailfromd.h b/mfd/mailfromd.h
index ac93cba4..bdf09dcd 100644
--- a/mfd/mailfromd.h
+++ b/mfd/mailfromd.h
@@ -309,5 +309,5 @@ 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 */
};
@@ -699,3 +699,3 @@ 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);
diff --git a/mfd/opcodes b/mfd/opcodes
index cee68d46..dd883ea3 100644
--- a/mfd/opcodes
+++ b/mfd/opcodes
@@ -92,3 +92,3 @@ 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 90a426eb..f8e58a92 100644
--- a/mfd/prog.c
+++ b/mfd/prog.c
@@ -1489,5 +1489,5 @@ instr_result(eval_environ_t env)
- 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);
@@ -1515,4 +1515,4 @@ instr_result(eval_environ_t env)
env->setreply(env->data, code, xcode, message);
- advance_pc(env, 3);
- adjust_stack(env, 1);
+ advance_pc(env, 1);
+ adjust_stack(env, 3);
}

Return to:

Send suggestions and report system problems to the System administrator.