Scripts for the External script condition. The Run Executable action runs its
programs from the same directory under the same rules.

This is the scripts directory: SCRIPTS_DIR in lib/defaults.cf is "scripts", so
external scripts are enabled out of the box. Rules can run only files inside
this directory. To disable external scripts set SCRIPTS_DIR empty in
etc/config.cf:

  SCRIPTS_DIR=

Two examples come with the installation. Both get the sender, the recipients and
the subject of the message and always end the same way, whatever the message
holds:

  accept.sh   exits with 0
  reject.sh   exits with 1

Condition: External script "reject.sh", arguments
  -- {message:envelope:sender} {message:envelope:recipients} {message:subject}
match on exit code equal 1. Action: Block message. accept.sh is set up the same
way with exit code equal 0. An update of the installation replaces accept.sh,
reject.sh and this README; scripts under other names are kept.

Put your own scripts here and run lib/fixperms.sh: the directory and its
scripts become owned by root and readable and executable by the MSH user,
which cannot change them.

Every script gets these environment variables:

  MSH_SENDER        envelope sender
  MSH_RECIPIENTS    all recipients, comma separated
  MSH_SUBJECT       subject
  MSH_MESSAGE_ID    Message-ID header
  MSH_MESSAGE_SIZE  size in bytes
  MSH_QUEUE_ID      Postfix queue id
  MSH_MESSAGE_FILE  the message as a .eml file, when the condition passes it
                    as a file or an argument uses {message_file}

The exit code and the output (up to 64 KB) decide whether the condition
matches. A script that runs longer than the condition's timeout is stopped,
together with the processes it started. A process the script leaves behind
holding its output open does not delay the message: a second after the script
ended the output received so far is used.

When a script is refused (outside the directory, not executable, writable by
others), cannot start, times out or finds no free slot (SCRIPTS_MAX_CONCURRENT)
the condition does not match, unless the rule says "When the script cannot run,
fails or times out: Match". The problem is in the processing trace of the
message and in the server log, once a minute per script and problem.

Values from the message are not to be trusted
---------------------------------------------

The variables and every argument resolved from the message (a subject, an
address, a header) are chosen by the sender. Control characters in them (NUL,
CR, LF, ...) are replaced by spaces before the script starts, and an argument
that resolves to nothing is passed as an empty string so the arguments after
it keep their positions. Still:

  - Prefer the MSH_* variables to arguments; quote them in the shell ("$MSH_SUBJECT").
  - When a value has to be an argument, put -- before it so a subject such as
    --config=/x is not read as an option:  my-check.sh -- {message:subject}
  - Never pass a value through a shell (eval, sh -c "...").
  - A rule that blocks mail on the script's verdict should use "When the
    script cannot run, fails or times out: Match", so a message the script
    could not check is not let through.
