diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-05-11 21:48:14 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-05-11 21:48:14 +0300 |
commit | f8323650568b0031f0ec4c50cde417cb10a48c56 (patch) | |
tree | b0007c30366860dc079a7bf8828d254b7486630f | |
parent | ffd2cf189d926abe00de0a79f292f1ea69d02aac (diff) | |
download | mailfromd-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-- | NEWS | 19 | ||||
-rw-r--r-- | doc/mailfromd.texi | 86 | ||||
-rw-r--r-- | mfd/drivers.c | 115 | ||||
-rw-r--r-- | mfd/gram.y | 131 | ||||
-rw-r--r-- | mfd/lex.l | 4 | ||||
-rw-r--r-- | mfd/mailfromd.h | 8 | ||||
-rw-r--r-- | mfd/opcodes | 2 | ||||
-rw-r--r-- | mfd/prog.c | 10 |
8 files changed, 276 insertions, 99 deletions
@@ -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); } @@ -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); } @@ -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 @@ -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); } |