# ZMailer Utilities Reference

There is considerable collection of various utilities in the ZMailer sources. Not all of them even become installed into your system in all situations.

## `zmailer` command script

The `zmailer` command script is a wrapper for driving various sub-utilities, and in some cases, honouring flags like freeze-state, which administrator may set to keep system down over reboots while some maintenance acitivity is under way.

Plain `zmailer` command is synonymous to `zmailer start`.

### `zmailer bootclean`

This removes all internal process reference PID files from `$`POSTOFFICE`/` directory.

Highly recommended for your system startup scripts before starting servers

### `zmailer start`

Without further parameters this starts the entire ZMailer system by starting subservers: `smtpserver`, `router`, and `scheduler`.

Giving parameter (one or more of subsystem names above) (re)starts just that (or those) subsystem(s).

If the system is in frozen state, start fails. See `zmailer freeze` below.

### `zmailer stop`, `zmailer kill`

Without further parameters, this terminates the main daemons of the ZMailer (`smtpserver`, `router`, and `scheduler`) by sending `SIGTERM` to them.

Giving parameter (one or more of subsystem names above) stops just that (or those) subsystem(s).

### `zmailer nuke`

Without further parameters, this kills the main daemons of the ZMailer. (`smtpserver`, `router`, and `scheduler`) by sending `SIGKILL` to them.

Giving parameter (one or more of subsystem names above) stops just that (or those) subsystem(s).

### `zmailer router`

Synonym to `zmailer start router`, (re)starts the `router` process(es).

### `zmailer scheduler`

Synonym to `zmailer start router`, (re)starts the `scheduler` process.

### `zmailer smtp(server)`

Synonym to `zmailer start smtpserver`, (re)starts the `smtpserver` process.

### `zmailer newdb`

A complicated subsystem on its own merits used to re-generate `router` configuration data for various database lookups.

This runs utility called `newdbprocessor` with its only argument of `$`MAILVAR`/db/dbases.conf`.

### `zmailer newal(iases)`

Re-generates `aliases` database, wrapper of `newaliases`, and superceded by `zmailer newdb`.

### `zmailer newf(qdnaliases)`

Re-generates `fqdnaliases` database, wrapper of `newfqdnaliases`, and superceded by `zmailer newdb`.

### `zmailer new-route(s)`

Compiles `routes` database, and superceded by `zmailer newdb`.

### `zmailer new-local(names)`

Compiles `localnames` database, and superceded by `zmailer newdb`.

### `zmailer logsync`

A special command sending signals to subsystems needing them for reopening their possibly long-living logfile opens.

To be used after other methods have rotated the logfiles to new names, but before anything further is done to them.

These days only the `scheduler` needs it, and if you have a choice, use this only after the scheduler logfile is rotated.

### `zmailer logrotate`

Runs ZMailer sub-utility rotate-logs.sh.

### `zmailer resubmit`

Moves files from `$`POSTOFFICE`/deferred/` to main `router` input directory (`$`POSTOFFICE`/router/`.)

### `zmailer cleanup`

Run this from your root crontab!

This cleans from `$`POSTOFFICE`/public/` files that are older than 2 days (48 hours), and from `$`POSTOFFICE`/postman/` files with names starting with a digit and aged over 7 days.

### `zmailer freeze`

This sets a flag which is honoured by subsystem start functions:

    # /opt/mail/bin/zmailer freeze
    freeze
    # /opt/mail/bin/zmailer router
    router Sorry, ZMailer is frozen, won't start anything until thawed !
    * CHECK THAT THE FREEZE CONDITION ISN'T DUE TO E.G. MAINTENANCE *

### `zmailer thaw`, `zmailer unfr(eeze)`

Thaws the previously frozen ZMailer system so that `zmailer start` will be able to start subsystems.

## The `newdbprocessor` script

FIXME! WRITEME! (See zapp-filefmts-database-fmts-dbases.conf for the configuration file)

    - Input file syntax
    - Supported database types, and what is done to them
    - Additional notes ?

## The `newdb` script

This is elementary wrapper script building binary databases with `makedb` utility into a temporary file, and replacing the old files with the new ones in proper order for the `router`'s automatic source change detecting relation parameter `-m` to work correctly.

      newdb

          -u
          -l

        -a
        -s
        -t dbtype
        /db/path/basename
        input-file-name

This script uses system `ZCONFIG` file to find out the desired database type, and derives the actual database file names from the variable.

Suffix selection rules are:

    dbm     .pag and .dir
    ndbm    .pag and .dir
    gdbm    .gdbm
    btree   .db
    bhash   .dbh

## The `makedb` utility

This utility is used by the ZMailer system to compile source files to various binary databases.

The way the ZMailer uses DBM entries is by using C-strings with their terminating `NUL` included at keys, and at data.. Thus the length is `strlen(string)+1`, not `strlen(string)` !

    WARNING: Policy data parsing does use unchecked buffers!

    \begin{verbatim}
    Usage: makedb [-apsvluA] dbtype database.name [infilename|-]
    \end{verbatim}

    -a: aliasinput
    -p: policyinput
    -A: append-mode
    -l: pre-lowercasify the key
    -u: pre-uppercasify the key
    -s: be silent
    -v: be verbose

    Dbtypes are: {\tt ndbm gdbm btree bhash}

    If no {\tt infilename} is defined, {\tt database.name} is assumed.

    \begin{description}
    \item[{\tt NDBM}] \mbox{}

    appends {\tt .pag}, and {\tt .dir}
    into the actual db file names.

    \item[{\tt GDBM}] \mbox{}

    {\bf does not} append {\tt .gdbm}
    into the actual db file name.

    \item[{\tt BTREE}] \mbox{}

    {\bf does not} append {\tt .db}
    into the actual db file name.

    \item[{\tt BHASH}] \mbox{}

    appends {\tt .pag}, and {\tt .dir}
    into the actual db file names.

    \end{description}

    The {\tt -a} option is for parsing input that comes in
    {\tt aliases} format: {\tt key: data,in,single,long,line}

## The `dblook` utility

    The way the ZMailer uses DBM entries is by using strings with
    their terminating {\tt NULL} as keys, and as data.. Thus the
    length is {\tt strlen(string)+1}, not {\tt strlen(string)} !

    \begin{verbatim}
    Usage: dblook [-dump] dbtype database.name [key]
    \end{verbatim}

    Dbtypes are: {\tt ndbm gdbm btree bhash}

    \begin{description}
    \item[{\tt NDBM}] \mbox{}

    appends {\tt .pag}, and {\tt .dir}
    into the actual db file names.

    \item[{\tt GDBM}] \mbox{}

    {\bf does not} append {\tt .gdbm}
    into the actual db file name.

    \item[{\tt BTREE}] \mbox{}

    {\bf does not} append {\tt .db}
    into the actual db file name.

    \item[{\tt BHASH}] \mbox{}

    appends {\tt .pag}, and {\tt .dir}
    into the actual db file names.

    \end{description}

    %\end{multicols}

    % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    \clearpage

## The `policy-builder.sh` script

    #! /bin/sh
    #
    # Sample smtp-policy-db builder script.
    #
    # This merges following files from $MAILVAR/db/ directory:
    #       smtp-policy.src
    #       localnames               ('= _localnames')
    #       smtp-policy.relay.manual ('= _full_rights')
    #       smtp-policy.relay        ('= _full_rights')
    #       smtp-policy.mx.manual    ('= _relaytarget')
    #       smtp-policy.mx           ('= _relaytarget')
    #       smtp-policy.spam         ('= _bulk_mail')
    #       smtp-policy.spam.manual  ('= _bulk_mail')
    #
    # These all together are used to produce files:  smtp-policy.$DBEXT
    # The produced database retains the first instance of any given key.
    #

    #FLAG=
    #while getopts n c; do
    #  case $c in
    #    n)       FLAG=$c;;
    #    ?)       exit 2;;
    #  esac
    #done
    #shift `expr $OPTIND - 1`

    ZCONFIG=@ZMAILERCFGFILE@
    . $ZCONFIG

    DBDIR="$MAILVAR/db/"
    USAGE="Usage: $0 [-n] [-d dbdir]"

    while [ "$1" != "" ]; do
        case "$1" in
            -n)
                FLAG=n
                ;;
            -d)
                shift
                DBDIR=$1
                if [ ! -d $DBDIR ]; then
                    echo $USAGE
                    exit 1
                fi
                ;;
            ?)
                echo $USAGE
                exit 0
                ;;
            *)
                echo $USAGE
                exit 2
                ;;
        esac
        shift
    done

    umask 022

    cd $DBDIR

    if [ ! -f smtp-policy.src ] ; then
            echo "No $DBDIR/smtp-policy.src input file"
            exit 64 # EX_USAGE
    fi

    # -- Former '-f' flag data (non)retrieval section removed

    # Fork off a subshell to do it all...
    (
      # The basic boilerplate
      cat smtp-policy.src

      # Localnames
      echo "# ----------"
      echo "# localnames:"
      cat localnames | \
      awk '/^#/{next;} NF >= 1 {printf "%s = _localnames\n",$1;}'

      # smtp-policy.relay
      # (Lists NETWORKS (NO DOMAINS!) that are allowed to use us as relay)
      # (well, actually it could also list e.g.: ".our.domain" if it would
      #  be fine to allow relaying from anybody whose IP address reverses to
      #  domain suffix ".our.domain")
      if [ -f smtp-policy.relay.manual ] ; then
        echo "# -------------------------"
        echo "# smtp-policy.relay.manual:"
        cat smtp-policy.relay.manual | \
        awk '/^#/{next;}
            {printf "%s = _full_rights\n",$0;next;}'
      fi
      if [ -f smtp-policy.relay ] ; then
        echo "# ------------------"
        echo "# smtp-policy.relay:"
        cat smtp-policy.relay | \
        awk '/^#/{next;}
            {printf "%s = _full_rights\n",$0;next;}'
      fi

      # smtp-policy.mx.manual
      # (Lists domains that are allowed to use us as inbound MX relay for them)
      if [ -f smtp-policy.mx.manual ] ; then
        echo "# ----------------------"
        echo "# smtp-policy.mx.manual:"
        cat smtp-policy.mx.manual | \
        awk '/^#/{next;} NF >= 1 {printf "%s = _relaytarget\n",$0;}'
      fi
      # smtp-policy.mx
      # (Lists domains that are allowed to use us as inbound MX relay for them)
      if [ -f smtp-policy.mx ] ; then
        echo "# ---------------"
        echo "# smtp-policy.mx:"
        cat smtp-policy.mx | \
        awk '/^#/{next;} NF >= 1 {printf "%s = _relaytarget\n",$0;}'
      fi

      # smtp-policy.spam
      # (Lists users, and domains that are known spam sources)
      # (We use file from "http://www.webeasy.com:8080/spam/spam_download_table"
      #  which is intended for QMAIL, and thus needs to be edited..)
      if [ -f smtp-policy.spam -o -f smtp-policy.spam.manual ] ; then
        echo "# ---------------------------"
        echo "# smtp-policy.spam{,.manual}:"
        ( if [ -f smtp-policy.spam ] ; then
            cat smtp-policy.spam
          fi
          if [ -f smtp-policy.spam.manual ] ; then
            cat smtp-policy.spam.manual
          fi ) | tr "[A-Z]" "[a-z]" | sed 's/^@//g' | sort | uniq | \
        awk '/^\[/{ # an address block to reject
                printf "%s  rejectnet +\n",$0;
                next;
            }
            NF > 0 { # All other cases are usernames with their domains
                printf "%s  = _bulk_mail\n",$0;
            }'
      fi

    # --------- end of subshell
    ) > smtp-policy.dat

    umask 022 # Make sure the resulting db file(s) are readable by all

    # Build the actual binary policy database (-p), and if the input
    # has same key repeating, append latter data instances to the first
    # one (-A):

    $MAILBIN/makedb -A -p $DBTYPE smtp-policy-new smtp-policy.dat || exit $?

    case $DBTYPE in
    dbm)
            mv smtp-policy-new.dir  smtp-policy.dir
            mv smtp-policy-new.pag  smtp-policy.pag
            ;;
    ndbm)
            mv smtp-policy-new.dir  smtp-policy.dir
            mv smtp-policy-new.pag  smtp-policy.pag
            ;;
    gdbm)
            mv smtp-policy-new.gdbm smtp-policy.gdbm
            ;;
    btree)
            mv smtp-policy-new.db   smtp-policy.db
            ;;
    esac

    exit 0

## `autoanswer`

The `autoanswer` program is intended to be placed into system global aliases database as following entry:

      autoanswer:  "| /path/to/MAILBIN/autoanswer"

It yields a reply message for all, except the error messages, nor to those with `X-autoanswer-loop:` header in them.

The reply sends back the original incoming message headers in the message body along with some commentary texts.

The program is, in reality, a perl script which can easily be tuned to local needs.

    #!@PERL@

    ##########################################################################
    #
    # Autoanswer.pl 1.0 for ZMailer 2.99.48+
    # (C) 1997 Telecom Finland
    #          Valtteri Karu <valtteri.karu@tele.fi>
    #
    # This program sends autoreply and the original headers to the originator
    # of the message. Version 2.99.48+ of the Zmailer is required for detecting
    # possible false addresses.
    #
    # USAGE:
    #
    # Create an alias for the address want to use:
    # autoreply: "|/path/to/autoanswer.pl"
    #
    ##########################################################################

    $nosend = 0;
    $double = 0;
    $address = $ENV{'SENDER'};

    if( ! -r "$ENV{'ZCONFIG'}") {
        LOG("zmailer.conf missing");
        exit 2;
    }

    open(ZMAILER,"< $ENV{'ZCONFIG'}" );
    while(<ZMAILER>) {
        chomp;
        split(/=/);
        $ZMAILER{$_[0]}=$_[1];
    }

    close ZMAILER;

    $logfile = $ZMAILER{'LOGDIR'} . "/autoanswer";

    while (<STDIN>) {

        $text = $_;

        if (($text eq "\n") && ( $double == 1)) {
    	last;
        }

        if (($text eq "\n") && ( $double == 0)) {
    	$double = 1;
    	next;
        }

        if ($text =~ m/^X-autoanswer-loop:/i) {
    	$nosend = 1;
    	LOG("Looping message, sender=$address");
        }

        $double = 0;

        push(@header,$text);
    }

    if (($address eq '<>') || $nosend ) {
        LOG("SENDER invalid");
        exit 1;
    }

    $outfile = $ZMAILER{'POSTOFFICE'} . "/public/autoanswer.$$";
    #$outfile = "/tmp/aa.$$";
    $now = time;
    $txttime = localtime(time);

    open(OUT,">$outfile");
    select(OUT);
    print "channel error\n";
    print "to $address\n";
    print "env-end\n";
    print "From: Autoreply service <postmaster>\n";
    print "To: $address\n";
    print "Subject: Autoreply\n";
    print "X-autoanswer-loop: Megaloop \n\n";
    print "      This is autoreply answer message by your request.\n\n";
    print "      Original message was received at UNIX time $now;\n";
    print "      which means '$txttime' in cleartext.\n\n";
    print "      Headers were:\n\n";
    print "------------------------------------------------------------------------------\n";
    print @header;
    print "------------------------------------------------------------------------------\n";
    print "\n      Have a nice day.\n";
    select(STDOUT);
    close OUT;
    $inode=(stat($outfile))[1];
    $newfile=$ZMAILER{'POSTOFFICE'} . "/router/$inode";
    rename($outfile, $newfile);
    LOG("Sent to $address");
    exit 0;

    sub LOG {

    	open(LOGf, ">>$logfile");
    	$ttime = localtime(time);
    	printf (LOGf "$ttime autoanswer: @_\n");
    	close LOGf;
    }

## `vacation`

`vacation` automatically replies to incoming mail. The canned reply is contained in the file `~/.vacation.msg`, that you should create in your home directory (or the file `Msgfile` specified by the `-m` option).

This file should include a header with at least a Subject: line (it should not include a To: line — if you want, you may include From: line, especially if you use the `-m` option), for example.

Usage:

- To start `vacation`, run the command `vacation start`. It will create a `~/.vacation.msg` file (if you don't already have one) in your home directory containing the message you want to send people who send you mail, and a `~/.forward` file in your home directory containing a line of the form:

        "\name", "|/opt/mail/bin/vacation name"

  where name is your login name. Make sure these files and your home directory are readable by everyone. Also make sure that no one else can write to them, and that no one can write to your home directory. Like this:

        chmod og-w $HOME $HOME/.forward

  )

  To stop vacation, run the command `vacation stop`. It will move the `~/.forward` file to `~/.vacforward`, and the automatic replies will stop.

        vacation 'start'
        vacation 'stop'
        vacation -I
        vacation
          -tN
          -mMsgfile
          -d
          user

Parameters:

- `-I`, `-i`

- initialize the `.vacation.pag`, and `.vacation.dir` files (or whatever the system supported database is), and start vacation.

  If the `-I` (or `-i`) flag is not specified, vacation tries to reply to the sender.

`-tN`

- Change the interval between repeat replies to the same sender. The default is one week. A trailing `s`, `m`, `h`, `d`, or `w` scales N to seconds, minutes, hours, days, or weeks respectively.

`-m`MsgFile``

- specifies the file in which the message to be sent is kept. The default is `$`HOME`/.vacation.msg`.

`-r`

- interval defines interval in days when not to answer again to the same sender. (Default is 1 day.)

`-d`

- disables the list of senders kept in the `.vacation.pag` and `.vacation.dir` files. (Or whatever database format is being used.)

Example:

-       Subject: I am on vacation

        I am on vacation until July 22. If you have something urgent, please
        contact Joe Jones (joe@blah.utoronto.ca). --john

  No message is sent if the user specified in the vacation command (if nothing is specified, it uses your username) does not appear explicitly in the To: or Cc: lines of the message, which prevents messages from being sent back to mailing lists and causing loops.

  A list of senders is kept in the files `~/.vacation.pag` and `~/.vacation.dir` in your home directory. These are dbm database files. (Note: not all database systems have two files, either may be missing.) The vacation message is in `~/.vacation.msg` and the automatic reply is activated by the `~/.forward` (and saved in `~/.vacforward`) The default vacation message is stored in `$MAILSHARE/vacation.msg`

  On machines running ZMailer, the name argument to `vacation` is optional, and the $USER environment variable is used to determine where to look for the message and the list of previous recipients.

  The `$SENDER` variable is checked first to determine the reply destination. It is normally set to the SMTP MAIL FROM: address or equivalent. This is an additional safeguard against sending replies to mailing lists, the PostMaster or the mailer daemon, since standards and common sense dictate that it never points back to an address that could cause a loop. The From line is used only as a last resort.