@c This file is part of the Mailfromd manual.
@c Copyright (C) 2007--2022 Sergey Poznyakoff
@c See file mailfromd.texi for copying conditions.
@c *******************************************************************
@pindex mtasim
  The @command{mtasim} utility is a @acronym{MTA} simulator for testing
@command{mailfromd} filter scripts.  By default it operates in
@dfn{stdio} mode, similar to that of @command{sendmail -bs}.  In this
mode it reads @acronym{SMTP} commands from standard input and sends
its responses to the standard output.  There is also another mode,
called @dfn{daemon}, where @command{mtasim} opens a @acronym{TCP}
socket and listens on it much like any @acronym{MTA} does.  In both
modes no actual delivery is performed, the tool only simulates the
actions an @acronym{MTA} would do and responses it would give.

  This tool is derived from the program @command{mta}, which I wrote
for GNU Anubis test suite.

@menu
* interactive mode::
* expect commands::
* traces::
* daemon mode::
* command summary::
* option summary::
@end menu

@node interactive mode
@section @command{mtasim} interactive mode mode

  If you start @command{mtasim} without options, you will see the
following:

@example
@group
220 mtasim (mailfromd @value{VERSION}) ready
(mtasim) _
@end group
@end example

@cindex readline
@cindex GNU Readline
  The first line is an usual @acronym{RFC} 2821 reply.  The second one is
a prompt, indicating that @command{mtasim} is in interactive mode and
ready for input.  The prompt appears only if the package is compiled
with GNU Readline and @command{mtasim} determines that its standard
input is connected to the terminal.  This is called @dfn{interactive
mode} and is intended to save the human user some typing by offering
line editing and history facilities (@pxref{Command Line Editing, ,
Command Line Editing, readline, GNU Readline Library}).  If the package
is compiled without GNU Readline, you will see:

@example
@group
220 mtasim (mailfromd @value{VERSION}) ready
_
@end group
@end example

@noindent
where @samp{_} represents the cursor.  Whatever the mode,
@command{mtasim} will wait for further input.

@cindex @code{HELP}, @command{mtasim} statement
  The input is expected to consist of valid @acronym{SMTP} commands
and special @command{mtasim} statements.  The utility will act exactly
like a @acronym{RFC} 2821-compliant @acronym{MTA}, except that it will
not do actual message delivery or relaying.  Try typing @code{HELP} to
get the list of supported commands.  You will see something similar
to:

@example
250-mtasim (mailfromd @value{VERSION}); supported SMTP commands:
250- EHLO
250- HELO
250- MAIL
250- RCPT
250- DATA
250- HELP
250- QUIT
250- HELP
250  RSET
@end example

 You can try a simple @acronym{SMTP} session now:

@example
220 mtasim (mailfromd @value{VERSION}) ready
(mtasim) @kbd{ehlo localhost}
250-pleased to meet you
250 HELP
(mtasim) @kbd{mail from: <me@@localhost>}
250 Sender OK
(mtasim) @kbd{rcpt to: <him@@domain>}
250 Recipient OK
(mtasim) @kbd{data}
354 Enter mail, end with `.' on a line by itself
(mtasim) @kbd{.}
250 Mail accepted for delivery
(mtasim) @kbd{quit}
221 Done
@end example

  Notice, that @command{mtasim} does no domain checking, so such thing
as @samp{rcpt to: <him@@domain>} was eaten without complaints.

@anchor{mtasim milter port}
@mtasimopt{port, described}
  So far so good, but what all this has to do with
@command{mailfromd}?  Well, that's what we are going to explain.  To
make @command{mtasim} consult any milter, use @option{--port}
(@option{-X}) command line option.  This option takes a single
argument that specifies the milter port to use.  The port can be given
either in the usual Milter format (@xref{milter port specification},
for a short description), or as a full @file{sendmail.cf} style
@code{X} command, in which case it allows to set timeouts as well:

@example
$ @kbd{mtasim --port=inet:999@@localhost}
# @r{This is also valid}:
$ @kbd{mtasim --port='mailfrom, S=inet:999@@localhost, F=T, T=C:100m;R:180s'}
@end example

  If the milter is actually listening on this port, @command{mtasim}
will connect to it and you will get the following initial prompt:

@example
@group
220-mtasim (mailfromd @value{VERSION}) ready
220 Connected to milter inet://localhost:999
(mtasim)
@end group
@end example

  Notice, that it makes no difference what implementation is listening
on that port, it may well be some other filter, not necessarily
@command{mailfromd}. 

@cindex @command{mtasim} auto mode
  However, let's return to @command{mailfromd}.  If you do not want to
connect to an existing @command{mailfromd} instance, but prefer
instead to create a new one and run your tests with it (a preferred
way, if you already have a stable filter running but wish to test a
new script without disturbing it), use @option{--port=auto}.  This
option instructs @command{mtasim} to do the following:

@enumerate 1
@item Create a unique temporary directory in @file{/tmp} and create a
communication socket within it.
@item Spawn a new instance of @command{mailfromd}.  The arguments and
options for that instance may be given in the invocation of
@command{mtasim} after a double-dash marker (@samp{--})
@item Connect to that filter.
@end enumerate

  When @command{mtasim} exits, it terminates the subsidiary
@command{mailfromd} process and removes the temporary directory it has
created.  For example, the following command will start
@command{mailfromd -I. -I../mflib test.rc}:

@example
@group
$ @kbd{mtasim -Xauto -- -I. -I../mflib test.rc}
220-mtasim (mailfromd @value{VERSION}) ready
220 Connected to milter unix:/tmp/mtasim-j6tRLC/socket
(mtasim)
@end group
@end example

@anchor{statedir mtasim option}
@mtasimopt{statedir, described}
  The @file{/tmp/mtasim-j6tRLC} directory and any files within it will
exist as long as @command{mtasim} is running and will be removed when
you exit from it.@footnote{However, this is true only if the program
is exited the usual way (via @code{QUIT} or end-of-file).  If it is
aborted with a signal like @code{SIGINTR}, the temporary directory is
not removed.}  You can also instruct the subsidiary
@command{mailfromd} to use this directory as its state directory
(@pxref{statedir}).  This is done by @option{--statedir} command line
option:

@example
$ @kbd{mtasim -Xauto --statedir -- -I. -I../mflib test.rc}
@end example

@noindent
(notice that @option{--statedir} is the @command{mtasim} option,
therefore it must appear @emph{before} @samp{--})

@mtasimopt{user, described}
@mtasimopt{group, described}
  Special care should be taken when using @command{mtasim} from root
account, especially if used with @option{-Xauto} and
@option{--statedir}.  The @command{mailfromd} utility executed by it
will switch to privileges of the user given in its configuration
(@pxref{Starting and Stopping}) and will not be able to create data in
its state directory, because the latter was created using @samp{root}
as owner.  To help in this case, @command{mtasim} understands
@option{--user} and @option{--group} command line options, that have
the same meaning as for @command{mailfromd}.  

  Now, let's try @code{HELP} command again:

@example
250-mtasim (mailfromd @value{VERSION}); supported SMTP commands:
250- EHLO
250- HELO
250- MAIL
250- RCPT
250- DATA
250- HELP
250- QUIT
250- HELP
250- RSET
250-Supported administrative commands:
250- \Dname=value [name=value...]       Define Sendmail macros
250- \Ecode                             Expect given SMTP reply code
250- \L[name] [name...]                 List macros
250- \Uname [name...]                   Undefine Sendmail macros
250  \Sfamily hostname address [port]   Define sender socket address
@end example

@cindex @command{mtasim} administrative commands
  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
letter.  Just like @acronym{SMTP} ones, administrative commands are
case-insensitive.  If a command takes arguments, the first argument
must follow the command letter without intervening whitespace.
Subsequent arguments can be delimited by arbitrary amount of
whitespace.

@anchor{D command}
@cindex D, @code{\D}, a @command{mtasim} command
@cindex @command{mtasim}, defining Sendmail macros
For example, the @code{\D} command defines Sendmail macros:

@example
(mtasim) @kbd{\Dclient_addr=192.168.10.1 f=sergiusz@@localhost i=testmsg}
(mtasim) 
@end example

@noindent
Notice that @command{mailfromd} does not send any response to the
command, except if there was some syntactic error, in which case it
will return a @samp{502} response.

@anchor{L command}
@cindex L, @code{\L}, a @command{mtasim} command
@cindex @command{mtasim}, listing Sendmail macros
  Now, you can list all available macros:

@example
(mtasim) @kbd{\L}
220-client_addr=192.168.10.1
220-f=sergiusz@@localhost
220 i=testmsg
(mtasim)
@end example

@noindent
or just some of them:

@example
(mtasim) @kbd{\Lclient_addr}
220 client_addr=192.168.10.1
(mtasim)
@end example

@cindex U, @code{\U}, a @command{mtasim} command
@cindex @command{mtasim}, undefining Sendmail macros
  To undefine a macro, use @command{\U} command:

@example
(mtasim) @kbd{\Ui}
(mtasim) @kbd{\l}
220-client_addr=192.168.10.1
220 f=sergiusz@@localhost
(mtasim)    
@end example

@anchor{S command}
@cindex S, @code{\S}, a @command{mtasim} command
@cindex @command{mtasim}, declare sender socket
  The @code{\S} command declare sender socket and host name.  These
parameters are passed to the @code{connect} handler, if one is
declared (@pxref{connect handler}).  To give you a chance to use this
command, @command{mtasim} does not invoke @code{connect} handler right
after connecting to the milter.  Instead it waits until either the
@code{\S} command or any @acronym{SMTP} command (except @samp{HELP})
is given.  After calling @code{connect} handler the @code{\S} is
disabled (to reflect it, it also disappears from the @code{HELP}
output).

  The @code{\S} command takes 1 to 4 arguments.  The first argument
supplies the socket family (@pxref{socket-families}).  Allowed values
are: @samp{stdio}, @samp{unix}, @samp{inet}, @samp{inet6} or
numbers from @samp{0} to @samp{3}.

  The @code{\S stdio} (or @code{\S 0}) command needs no additional
arguments.  It indicates that the @acronym{SMTP} connection is
obtained from the standard input.  It is the default if sender socket
is not declared explicitly.

  The command @code{\S unix} indicates that the connection is accepted
from a UNIX socket.  It requires two more argument.  The first one
supplies sender host name and the second one supplies full
path name of the socket file.  For example:

@example
\S unix localhost /var/run/smtp.sock
@end example

  The commands @code{\S inet} and @code{\S inet6} indicate that the
connection came from an @samp{INET} IPv4 or IPv6 socket,
correspondingly@footnote{Depending on how @command{mailfromd} is
configured, @samp{inet6} may be not available.}.  They require all
four arguments to be specified.  The additional arguments are:
host name, IP address, and port number, in that order.  For example:

@example
\S inet relay.gnu.org.ua 213.130.31.41 34567
@end example

or

@example
\S inet6 relay.gnu.org.ua 2001:470:1f0a:1be1::2 34567
@end example

  Sender socket address can also be configured from the command
line (@pxref{option summary, sender-socket}).  

  Now, let's try a real-life example.  Suppose you wish to test the
greylisting functionality of the filter script described in @ref{Filter Script
Example}.  To do this, you start @command{mtasim}:

@example
$ @kbd{mtasim -Xauto -- -I. -I../mflib test.rc}
220-mtasim (mailfromd @value{VERSION}) ready
220 Connected to milter unix:/tmp/mtasim-ak3DEc/socket
(mtasim)
@end example

  The script in @file{test.rc} needs to know @code{client_addr} macro,
so you supply it to @command{mtasim}:

@example
(mtasim) @kbd{\Dclient_addr=10.10.1.13}
@end example

Now, you try an @acronym{SMTP} session:

@example
(mtasim) @kbd{ehlo yahoo.com}
250-pleased to meet you
250 HELP
(mtasim) @kbd{mail from: <gray@@yahoo.com>}
250 Sender OK
(mtasim) @kbd{rcpt to: <gray@@localhost>}
450 4.7.0 You are greylisted for 300 seconds
@end example

  OK, this shows that the greylisting works.  Now quit the session:

@example
(mtasim) @kbd{quit}
221 Done
@end example

@node expect commands
@section @command{mtasim} expect commands

@cindex expect mode, @command{mtasim}
@cindex @command{mtasim} expect mode
@cindex E, @code{\E}, a @command{mtasim} command
  Until now we were using @command{mtasim} interactively.  However, it
is often useful in shell scripts, for example the @command{mailfromd}
test suite is written in shell and @command{mtasim}.  To avoid the
necessity to use auxiliary programs like @command{expect} or
@command{DejaGNU}, @command{mtasim} contains a built-in expect
feature.  The administrative command @code{\E} introduces the
@acronym{SMTP} code that the next command is expected to yield.  For
example,

@example
@group
\E250
rcpt to: <foo@@bar.org>
@end group
@end example

@noindent
tells @command{mtasim} that the response to @code{RCPT TO} command
must begin with @samp{250} code.  If it does, @command{mtasim}
continues execution.  Otherwise, it prints an error message and
terminates with exit code 1.  The error message it prints looks like:

@example
Expected 250 but got 470
@end example

  The expect code given with the @code{\E} command may have less than
3 digits.  In this case it specifies the first digits of expected
reply.  For example, the command @samp{\E2} matches replies
@samp{200}, @samp{220}, etc. 

  This feature can be used to automate your tests.  For example, the
following script tests the greylisting functionality (see the previous
section):

@example
# @r{Test the greylisting functionality}
#
\E220
\Dclient_addr=10.10.1.13
\E250
ehlo yahoo.com
\E250
mail from: <gray@@yahoo.com>
\E450
rcpt to: <gray@@localhost>
\E221
quit
@end example

@cindex @command{mtasim}, using in shell scripts
  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:

@example
@group
mtasim -Xauto --statedir -- -I. -I../mflib test.rc < @file{scriptfile}
if $? -ne 0; then
  echo "Greylisting test failed"
fi
@end group
@end example

@node traces
@section Trace Files

@cindex trace file, @command{mtasim}
  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: 

@table @option
@mtasimopt{trace-file, described}
@item --trace-file=@var{file}
  Sets the name of the trace file, i.e. a file to which the session
transcript will be written.  Both the input commands, and the
@command{mtasim} responses are logged.  If the file @var{file} exists,
it will be truncated before logging.  This, however, can be changed
using the following option:

@mtasimopt{append, described}
@item -a
@itemx --append
  If the trace file exists, append new trace data to it.
@end table

@node daemon mode
@section Daemon Mode

@mtasimopt{daemon, described}
@cindex @command{mtasim} daemon mode
  To start @command{mtasim} in @dfn{daemon} mode, use the
@option{--daemon} (or @option{-bd}) command line option.  This mode
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 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.

  This mode is designed for use in shell scripts and automated test
cases.

@node command summary
@section Summary of the @command{mtasim} Administrative Commands

This section provides a summary of administrative commands available
in @command{mtasim}.

@deffn {mtasim command} \D name=value [name=value...]
Defines Sendmail macro @var{name} to the given @var{value}.  Any number
of @var{name}=@var{value} pairs can be given as arguments.

@xref{D command}.
@end deffn

@deffn {mtasim command} \E code
Instructs @command{mtasim} to expect next @acronym{SMTP} command to
return given @var{code} (a three-digit decimal number).

@xref{expect commands}.
@end deffn

@deffn {mtasim command} \L [name...]
Lists defined macros.  @xref{L command}.
@end deffn

@deffn {mtasim command} \U name [name...]
Undefines macros given as its arguments.
@end deffn

@deffn {mtasim command} \S family [hostname address [port]]
Declares the sender socket parameters.  @xref{S command}, for a
detailed description and examples.

This command is available only at the initial stage of a
@command{mtasim} session, before the first @acronym{SMTP} command was
given.  It is disabled if the @option{--sender-socket} option was
given in the command line (@pxref{option summary, sender-socket}).
The @code{help} output reflects whether or not this command is available.

If neither this command nor the @option{--sender-socket} option were
given, @command{mtasim} behaves as if given the @code{\S stdio}
command.

The @var{family} argument
supplies the socket family, i.e. the first argument to the
@code{connect} handler (@pxref{connect handler}).  It can have either
literal or numeric value, as described in the table below:

@float Table, S-command-socket-families
@caption{Socket families}
@multitable @columnfractions 0.25 .10 0.45
@headitem Literal @tab Numeric @tab Meaning
@item stdio @tab  0    @tab Standard input/output (the @acronym{MTA} is
run with @option{-bs} option) 
@item unix  @tab  1    @tab @acronym{UNIX} socket
@item inet  @tab  2    @tab @acronym{IP}v4 protocol
@item inet6 @tab  3    @tab @acronym{IP}v6 protocol
@end multitable
@end float

See also @ref{socket-families}.

Depending on the @var{family}, the rest of arguments supply additional
parameters:

@table @asis
@item stdio
The @var{hostname} argument can be specified.  It defines the first
argument of the @code{connect} handler (@pxref{hostname in connect
handler}).

@item inet
@itemx inet6
All arguments must be specified.

@multitable @columnfractions 0.4 0.2 0.4
@headitem argument @tab @code{connect} argument @tab meaning
@item hostname @tab 1 @tab Sender host name
@item address  @tab 4 @tab Sender IP address
@item port     @tab 3 @tab Sender port number
@end multitable

@item unix
@var{Hostname} and @var{address} must be supplied.  The @var{address}
argument must be a full pathname of the UNIX socket.
@end table

@end deffn

@node option summary
@section @command{mtasim} command line options

  This section summarizes all available @command{mtasim} command line options.

@table @option
@mtasimopt{append, summary}
@mtindex a, -a, @command{mtasim} option, summary
@item --append
@itemx -a 
  Append to the trace file.  @xref{traces}.

@mtasimopt{body-chunk, summary}  
@item --body-chunk=@var{number}
  Set the body chunk length (bytes) for @code{xxfi_body} calls.

@mtasimopt{daemon, summary}
@mtindex bs, -bd, @command{mtasim} option, summary
@item --daemon
@itemx -bd
  Run as daemon.  @xref{daemon mode}.

@mtasimopt{define, summary}
@mtindex D, -D, @command{mtasim} option, summary
@item --define=@var{macro}=@var{value}
@itemx -D @var{macro}=@var{value}
  Define Sendmail macro @var{macro} to the given @var{value}.  It is
similar to the @code{\D} administrative command (@pxref{D command})

@mtasimopt{gacopyz-log, summary}
@item --gacopyz-log=@var{level}
Set desired logging level for @command{gacopyz} library
(@pxref{Gacopyz}).  @xref{gacopyz-log option}, for a detailed
description of @var{level}.  Notice, that unless this option is used,
the @option{--verbose} (@option{-v}) command line option implies
@option{--gacopyz-log=debug}.

@mtasimopt{group, summary}
@mtindex g, -g, @command{mtasim} option, summary
@item --group=@var{name}
@itemx -g @var{name}
When switching to user's privileges as requested by the
@option{--user} command line option, retain the additional group
@var{name}.  Any number of @option{--group} options may be given to
supply a list of additional groups.

@mtasimopt{user, summary}
@mtindex u, -u, @command{mtasim} option, summary
@item --user=@var{name}
@itemx -u @var{name}
Run with this user privileges.  This option and the @option{--group}
option have effect only if @command{mtasim} was started with root
privileges. 

@item --help
@itemx -?
  Display a short help summary

@mtasimopt{milter-version, summary}
@item --milter-version=@var{version}
  Force using the given Milter protocol version number.  The
@var{version} argument is either a numeric version (e.g.@: @samp{2}), or
a version string in form @samp{@var{major}.@var{minor}[.@var{patch}]},
where square brackets indicate optional part.  The default is
@samp{1.0.0}.  If @var{version} is any of @samp{2}, @samp{3} or
@samp{1.0.0}, the default protocol capabilities and actions for that
version are set automatically.  This option
is intended for development and testing of the Gacopyz library
(@pxref{Gacopyz}).

@mtasimopt{milter-proto, summary}
@item --milter-proto=@var{bitmask}
Set Milter protocol capabilities.  See @file{gacopyz/gacopyz.h} for
the meaning of various bits in the @var{bitmask}.  Look for the C
macros with the prefix @samp{SMFIP_}.

@mtasimopt{milter-timeout, summary}
@item --milter-timeout=@var{values}
Set timeouts for various Milter operations.  @var{Values} is a
comma-separated list of assignments @samp{@var{T}=@var{V}}, where
@var{T} is a @dfn{timeout code}, indicating which timeout to set, and
@var{V} is its new value.  Valid timeout codes are:

@table @asis
@item C
Timeout for connecting to a filter.

@item W
@itemx S
Timeout for sending information from the simulator to a filter.

@item R
Timeout for reading reply from the filter.

@item E
Overall timeout between sending end-of-message to filter and receiving
final acknowledgment.  Indirectly, it configures the upper
limit on the execution time of the @code{eom} handler (@pxref{eom handler}).
@end table

@mtasimopt{milter-actions, summary}
@item --milter-actions=@var{bitmask}
Set Milter actions.  See @file{gacopyz/gacopyz.h} for
the meaning of various bits in the @var{bitmask}.  Look for the C
macros with the prefix @samp{SMFIF_}.

@mtasimopt{no-interactive, summary}
@item --no-interactive
  Not-interactive mode (disable readline).  @xref{Command Line Editing, ,
Command Line Editing, readline, GNU Readline Library}.

@mtasimopt{port, summary}
@mtindex X, -X, @command{mtasim} option, summary
@item --port=@var{port}
@itemx -X @var{port}
  Communicate with given Milter @var{port}.  @xref{mtasim milter
port}.

@mtasimopt{prompt, summary}
@item --prompt=@var{string}
  Set readline prompt.  The default prompt string is @samp{(mtasim) }.

@mtasimopt{sender-socket, summary}
@item --sender-socket=@var{family}[,@var{hostname},@var{address}[,@var{port}]]
  Declare sender socket address.  This option has the same effect as
the @code{S command}.  @xref{S command}, for a detailed discussion
and a description of its arguments.

@mtasimopt{statedir, summary}
@item --statedir
  When using @option{-Xauto}, use the temporary directory name as
@command{mailfromd} state directory (@pxref{statedir mtasim option}).

@mtasimopt{stdio, summary}
@mtindex bs, -bs, @command{mtasim} option, summary
@item --stdio
@itemx -bs
  Use the @acronym{SMTP} protocol on standard input and output.  This
is the default mode for @command{mtasim}.  @xref{interactive mode}.

@mtasimopt{trace-file, summary}
@item --trace-file=@var{file}
  Set name of the trace file.  @xref{traces}.

@item --usage
  Display option summary

@mtasimopt{verbose, summary}  
@mtindex v, -v, @command{mtasim} option, summary
@item --verbose
@itemx -v
  Increase verbosity level.  Implies @option{--gacopyz-log=debug},
unless that option is used explicitly.

@item --version
@itemx -V
  Print program version

@end table


