Provided by: miltertest_2.11.0~beta2-9build4_amd64 bug

NAME

       miltertest - milter unit test utility

SYNOPSIS

       miltertest [-D name[=value]] [-s script] [-u] [-v] [-V] [-w]

DESCRIPTION

       miltertest  simulates the MTA side of an MTA-milter interaction for testing a milter-aware
       filter application.  It takes as input a script using the Lua language, and  by  exporting
       some  utility  functions,  makes  it  possible  for users to write scripts that exercise a
       filter.

       See documentation on Lua (e.g. http://www.lua.org) for  the  syntax  of  the  language  in
       general.   The  documentation  below  describes  functions  that  are added to Lua by this
       application to make testing possible.

       Documentation on milter can be found at http://www.milter.org.  A  particular  transaction
       must  follow  a series of steps to be completed, namely negotiate, connection information,
       envelope sender, envelope recipient(s), header  field(s),  end-of-header,  body  chunk(s),
       end-of-message.   To  make the work of writing tests with miltertest simpler, any of these
       steps prior to end-of-message that is skipped will  be  filled  in  using  arbitrary,  but
       legal, data.

       Interspersed with these protocol phases are optional macro (key/value) deliveries from the
       MTA.  miltertest will never send these automatically.  If they are needed for your  tests,
       you must send them as part of your test script.

OPTIONS

       -D name[=value]
              Defines  a  global  variable  called  name  to  the Lua interpreter.  If a value is
              provided, the global variable is set to that value (as a string, although  Lua  can
              convert  strings  to  numbers  internally).   If  no  value is provided, the global
              variable is set to 1.

       -s script
              Use the contents of file script as the Lua script to be executed.  The  default  is
              to read from standard input.

       -u     After  the filter being tested is terminated, report user and system time consumed.
              See getrusage(2).

       -v     Increase verbose output.  May be specified multiple times to request more and  more
              information.

       -V     Print version number and exit.

       -w     Don't wait for child status to be returned when testing is complete.

FUNCTIONS

       The  following  functions  are made available to Lua scripts for exercising a filter.  All
       functions return Lua constant "nil" on success or  an  error  string  on  failure,  unless
       otherwise indicated.

       mt.abort(conn)
              Aborts the transaction in progress on the specified connection.

       mt.bodyfile(conn, file)
              Sends  the  contents of the named file to the connection as body data.  If there is
              any error opening file for reading, the test aborts.

       mt.bodyrandom(conn, n)
              Sends at least n bytes of random-length lines of random  printable  ASCII  data  as
              body chunks to the specified connection.

       mt.bodystring(conn, str)
              Sends str as a chunk of body text on the specified connection.

       mt.chdir(directory)
              Changes the current working directory to the named directory.

       mt.connect(sockinfo[, count, interval])
              Makes  a  connection  to  a  filter  listening at the socket described by sockinfo.
              Returns a handle referring to that connection, or the Lua constant "nil" on  error.
              If  count  and  interval  are  included, they specify the number of times to try to
              connect to the filter and the  delay  between  each  connection  in  seconds  (with
              floating    point    values    permitted).     If    the    environment    variable
              MILTERTEST_RETRY_SPEED_FACTOR is set and appears to contain an integer,  the  value
              of  interval  (if  set)  will  be multiplied by the value found in that environment
              variable.  This is included to allow tests in a  large  test  suite  to  be  easily
              adjusted on slow systems without reconfiguring the entire test suite.

       mt.conninfo(conn, host, ip)
              Sends information about a new SMTP connection to the MTA, represented by connection
              conn, from the host named host at IP address ip (both strings).  If host is the Lua
              constant  "nil",  the  string  "localhost"  is  assumed.  If ip is the Lua constant
              "nil", a DNS query will be made for the IP address matching host; if none is found,
              the  test  will  abort.  The ip may also be the special string "unspec", which will
              tell the filter that a connection came in from an unknown protocol family.

       mt.data(conn)
              Announces the DATA command on the specified connection, which  occurs  between  the
              last RCPT TO command and the beginning of the header block.

       mt.disconnect(conn[, polite]))
              Sends a "quit" message to the specified connection and then closes that connection.
              The specified conn handle should no longer be used.  If polite is defined, it  must
              be  a  Boolean  indicating  whether a normal disconnect should be done (true) or an
              abrupt disconnect should be done (false).   An  abrupt  disconnect  skips  standard
              protocol shutdown steps.

       mt.echo(string)
              Prints the specified string on standard output.  Returns nothing.

       mt.eoh(conn)
              Announces end-of-header on the specified connection.

       mt.eom(conn)
              Announces  end-of-message  on  the  specified  connection, and begins capturing any
              other operations the filter might perform in that phase.

       mt.eom_check(conn, op, param[, ...])
              Checks the captured set of EOM operations (see above) to determine whether  or  not
              specific  milter  actions  were  requested  by the filter.  Returns a Boolean value
              (true or false).  See the EOM CHECKS section for details.

       mt.getheader(conn, hdr, n)
              Retrieves the value of the nth instance of header field named hdr added during end-
              of-message  processing on the specified connection.  This can be used by the script
              to verify that the header thus added contains the right thing.  Returns  the  value
              as a string, or the Lua constant "nil" on error.

       mt.getcwd()
              Returns the current working directory as a string.

       mt.getreply(conn)
              Returns  the  last  milter  reply  received  from  the  specified connection, as an
              integer.  This can be compared to any of the SMFIR_* constants defined by milter to
              see  if  the filter responded as expected.  This value is initially set to the NULL
              character.

       mt.header(conn, name, value)
              Sends the header with the given name and value to the specified connection.

       mt.helo(conn, name)
              Sends HELO/EHLO information using the specified name as the parameter given.

       mt.macro(conn, type, name, value[, name2, value2[, ...]])
              Declares a macro called name whose value is value and whose type (matching protocol
              element)  is  type.   Valid  types  are  SMFIC_CONNECT,  SMFIC_HELO, SMFIC_MAIL and
              SMFIC_RCPT.  Multiple macro names and values can be provided, but they must  appear
              in pairs.

       mt.mailfrom(conn, envfrom[, ...])
              Announces  envfrom  as  the  envelope sender of a new message.  ESMTP parameters as
              additional arguments are permitted.

       mt.negotiate(conn, version, actions, steps)
              Performs milter option negotiation with the connection conn, advertising  that  the
              specified  protocol version, protocol actions and protocol steps are offered by the
              MTA.  Returns the Lua constant "nil" on success or an error string on failure.   If
              any  of  the  protocol  parameters  are  "nil",  the  current  defaults (defined in
              libmilter/mfdef.h, provided with the milter source code) will be used.

       mt.rcptto(conn, envrcpt[, ...])
              Announces envrcpt as an envelope recipient  of  a  message.   ESMTP  parameters  as
              additional arguments are permitted.

       mt.set_timeout(n)
              Sets the read timeout to n seconds.  The default is ten seconds.  Returns nothing.

       mt.sleep(n)
              Sleeps  for  n  seconds.   The  value  may  be  an integer (for whole seconds) or a
              floating-point value (for partial seconds).

       mt.signal(n)
              Sends the specified signal number n to the running filter.

       mt.startfilter(path, arg1, arg2, ...)
              Starts the filter whose binary is located at path with argument vector comprised of
              strings  path,  arg1,  arg2,  etc.   Basically  this  is  almost the same syntax as
              execl(3) except that miltertest also does the fork for you, and will  remember  the
              process  ID  in  order to request a clean shutdown using SIGTERM and wait(2) at the
              end of the test script.  If the filter  could  not  be  started,  an  exception  is
              generated with an error message returned.

       mt.test_action(conn, action)
              Tests  whether  or  not  the connection represented by conn requested the specified
              milter  protocol  action,  specified  by  an  SMFIF_*   constant,   during   option
              negotiation.  (See the libmilter documentation and/or include files for details.)

       mt.test_option(conn, option)
              Tests  whether  or  not  the connection represented by conn requested the specified
              milter  protocol  option,  specified  by  an  SMFIP_*   constant,   during   option
              negotiation.  (See the libmilter documentation and/or include files for details.)

       mt.unknown(conn, str)
              Announces that the unknown SMTP command str arrived over the connection represented
              by conn.

EOM CHECKS

       The mt.eom_check() function is used to determine what changes to the  message  the  filter
       requested  during its EOM callback.  The changes can be requested in any order.  The first
       parameter, op, indicates what operation is of interest, and  it  also  dictates  what  the
       possible  parameter  list  is.   Valid  values  and corresponding parameters for op are as
       follows:

       MT_HDRADD
              Checks to see if a header field was added to the message.   If  no  parameters  are
              given,  the  function returns true if any header field was added.  If one parameter
              was given, the function returns true only if  the  named  header  field  was  added
              (regardless  of its value).  If two parameters are given, the function returns true
              only if the named header field was added with the specified value.

       MT_HDRCHANGE
              Checks to see if an existing header field was changed.  If no parameters are given,
              the  function  returns true if any header field was modified.  If one parameter was
              given, the function returns true only  if  the  named  header  field  was  modified
              (regardless  of  its new value).  If two parameters are given, the function returns
              true only if the named header field was modified to have the specified new value.

       MT_HDRDELETE
              Checks to see if an existing header field was deleted.  If no parameters are given,
              the  function  returns  true if any header field was deleted.  If one parameter was
              given, the function returns true only if the named header field was deleted.

       MT_HDRINSERT
              Checks to see if a header field was inserted into the message.   If  no  parameters
              are  given,  the  function  returns  true  if  any  header field was added.  If one
              parameter was given, the function returns true only if the named header  field  was
              added (regardless of its value).  If two parameters are given, the function returns
              true only if the named header field was added with the specified value.   If  three
              parameters  are given, the function returns true only if the named header field was
              added with the specified value at the specified index.

       MT_RCPTADD
              Checks to see if an envelope recipient was added.  Currently only one parameter may
              be provided.

       MT_RCPTDELETE
              Checks  to  see if an envelope recipient was deleted.  Currently only one parameter
              may be provided.

       MT_BODYCHANGE
              Checks to see if the message's  body  was  replaced  by  other  content.   With  no
              parameters,  the  function returns true only if the body was changed (regardless of
              the new content).  With one parameter, the function returns true only if  the  body
              was changed to the specified new content.

       MT_QUARANTINE
              Checks  to  see  if  the  filter  requested  quarantining  of the message.  With no
              parameters, the function returns true only if quarantine was requested.   With  one
              parameter,  the  function  returns  true  only if quarantine was requested with the
              specified reason string.

       MT_SMTPREPLY
              Checks to see if the filter requested a  specific  SMTP  reply  message.   With  no
              parameters, the function returns true only if a specific reply was requested.  With
              one parameter, the function returns true only if a  specific  reply  was  requested
              with  the specified SMTP code.  With two parameters, the function returns true only
              if a specific reply was requested with the specified SMTP code and enhanced  status
              code.   With  three  parameters, the function returns true only if a specific reply
              was requested with the specified SMTP code, enhanced status code, and text.

EXAMPLE

       -- Echo that the test is starting
       mt.echo("*** begin test")
       -- start the filter
       mt.startfilter("../myfilter", "-p", "inet:12345@localhost")
       mt.sleep(2)

       -- try to connect to it
       conn = mt.connect("inet:12345@localhost")
       if conn == nil then
            error "mt.connect() failed"
       end

       -- send connection information
       -- mt.negotiate() is called implicitly
       if mt.conninfo(conn, "localhost", "127.0.0.1") ~= nil then
            error "mt.conninfo() failed"
       end
       if mt.getreply(conn) ~= SMFIR_CONTINUE then
            error "mt.conninfo() unexpected reply"
       end

       -- send envelope macros and sender data
       -- mt.helo() is called implicitly
       mt.macro(conn, SMFIC_MAIL, "i", "test-id")
       if mt.mailfrom(conn, "user@example.com") ~= nil then
            error "mt.mailfrom() failed"
       end
       if mt.getreply(conn) ~= SMFIR_CONTINUE then
            error "mt.mailfrom() unexpected reply"
       end

       -- send headers
       -- mt.rcptto() is called implicitly
       if mt.header(conn, "From", "user@example.com") ~= nil then
            error "mt.header(From) failed"
       end
       if mt.getreply(conn) ~= SMFIR_CONTINUE then
            error "mt.header(From) unexpected reply"
       end
       if mt.header(conn, "Date", "Tue, 22 Dec 2009 13:04:12 -0800") ~= nil then
            error "mt.header(Date) failed"
       end
       if mt.getreply(conn) ~= SMFIR_CONTINUE then
            error "mt.header(Date) unexpected reply"
       end
       if mt.header(conn, "Subject", "Signing test") ~= nil then
            error "mt.header(Subject) failed"
       end
       if mt.getreply(conn) ~= SMFIR_CONTINUE then
            error "mt.header(Subject) unexpected reply"
       end
       -- send EOH
       if mt.eoh(conn) ~= nil then
            error "mt.eoh() failed"
       end
       if mt.getreply(conn) ~= SMFIR_CONTINUE then
            error "mt.eoh() unexpected reply"
       end

       -- send body
       if mt.bodystring(conn, "This is a test!\r\n") ~= nil then
            error "mt.bodystring() failed"
       end
       if mt.getreply(conn) ~= SMFIR_CONTINUE then
            error "mt.bodystring() unexpected reply"
       end
       -- end of message; let the filter react
       if mt.eom(conn) ~= nil then
            error "mt.eom() failed"
       end
       if mt.getreply(conn) ~= SMFIR_ACCEPT then
            error "mt.eom() unexpected reply"
       end

       -- verify that a test header field got added
       if not mt.eom_check(conn, MT_HDRINSERT, "Test-Header") then
            error "no header added"
       end

       -- wrap it up!
       mt.disconnect(conn)

NOTES

       If a filter negotiates one of the SMFIP_NO* protocol option bits and a script attempts  to
       perform one of those protocol steps, an error is returned.  It is up to the test author to
       use mt.test_option() function to see if performing a protocol  step  has  been  explicitly
       disabled by the filter.

MILTER NOTES

       When  mt.macro() is called, it replaces all previous macros of the same type with the ones
       provided in the argument list.  Thus, one call should be made that lists the complete  set
       rather  than  one  call per name-value pair.  Also, as each stage in the milter process is
       executed, all macros corresponding stages  after  the  current  one  are  discarded.   For
       example,  calling  mt.helo(), which corresponds to SMFIC_HELO, will cause all prior macros
       of type SMFIC_MAIL and SMFIC_RCPT to be discarded as they represent a  milter  stage  that
       comes later than SMFIC_HELO.

       Since  the  milter  protocol  and  the  internals  of  libmilter  itself  are not formally
       documented, there are myriad other subtleties of the milter  protocol  and  implementation
       that are not documented here and may not be documented elsewhere, and could change without
       notice.  Caveat emptor.

VERSION

       This man page covers version 1.5.0 of miltertest.

COPYRIGHT

       Copyright (c) 2009-2014, The Trusted Domain Project.  All rights reserved.

SEE ALSO

       Milter -- http://www.milter.org

       Lua -- http://www.lua.org

                                    The Trusted Domain Project                      miltertest(8)