# The .maildelivery File in Detail

Before we get started, this is a good place (as good as any...) to mention an nmh feature. slocal will add a `Delivery-Date:` header field as it delivers each message -- no matter how it's delivered. MH only adds them to messages delivered to a file. `Delivery-Date:` can be handy with scan, pick, and other commands that let you act on any header field. For instance, you could write a scan format file to display the date and time a message was sent as well as the delivery date and time.

Each entry of .maildelivery has five arguments (except with the undocumented slocal `select` feature). There's either a comma or one or more spaces between each argument. If an argument has space or a comma in it, put double quotes (`"`) around the argument. To include a literal double quote, type `\"` (put a backslash first).

This chapter uses eight normal words precisely, with an exact meaning. Because those words are used so often, I decided not to italicize them anywhere except where they're defined. The words are: argument, field, pattern, action, result, string, succeed, and delivered. The definitions are below.

The five arguments on a .maildelivery entry, explained in the following five sections, are:

`field pattern action result string`

There are a few other things to mention first:

- When an action writes to a file, the action succeeds if the file is written successfully. When an action runs a command, the action succeeds if the command returns a zero (normal) exit status.
- If an action succeeds, the message will usually be marked as delivered. The next item explains what I mean by "usually."
- Entries in .maildelivery can test to see whether a message was delivered by previous entries. For instance, the `?` on line 6 of the Example Simple .maildelivery file tests for delivery. If either line 2 or line 4 delivered the message, line 6 won't add the message to my system mailbox.

  But -- just because an action succeeds, that does not mean the message has been marked "delivered." For instance, I could add a new line 5 to the Example Simple .maildelivery file. The entry would run rcvtty to tell me about new messages that come in. I wouldn't want this action to "deliver" the message; if it did, the messages I'd been notified about would never get into my system mailbox. As you'll see later in this section, using an R result on an entry means that even a successful action won't mark a message delivered.

- The versions of slocal I've used have some rough edges. Some things don't work quite the way the manual page says they will. Others are undocumented. There are some small differences in the way that .maildelivery files are handled by MMDF and by slocal.

  Simple .maildelivery files usually work just fine. If yours doesn't work the way you think it should, the tips in the Section Debugging Tips may help.

> NOTE: Many pathnames in these examples start with `/x/y`. That pathname is system-dependent; it's typically the MH library directory. If you can't find rcvstore and other commands in this chapter, ask your system administrator.

Now, let's look at the five arguments in each entry.

## First .maildelivery Argument: Field

The first argument, field, refers to a header field in the message. In line 2 of the Example Simple .maildelivery file, for instance, the field argument `from` will match the `From:` field in a message header. You can specify any field that might be in a message header, including ones you make up arbitrarily (like `X-auto-m-p:` in the Section Handing Periodic Mail).

You can't always do what you need to by matching header fields, though. Maybe you get mail from several addresses or aliases, and you want to match any of those messages with one entry. Or, you might want to match all messages. So, the field can also have one of these four special values:

`addr`
  Matches the address that was used to cause delivery to the recipient. It's typically your username (mine is jerry). This is explained in the Section System Aliases, the to and addr Fields.
`source`
  Matches the out-of-band sender information, the envelope sender. This is an address, supplied by the system's mail transfer agent. You'll usually see it as the first line of a message file starting with `From somewhere`, or stored in a `Return-Path:` header field.

  The `source` is handy for matching mail from mailing lists. This doesn't work for all mailing lists, but it works for many. I'll use the mh-users list as an example. Message headers in mh-users have header fields like these:

            From mh-users-request@ics.uci.edu  Mon Jan 09 23:43:16 1995
            Date: Mon, 09 Jan 1995 23:43:16 GMT
            From: Some Person <someperson@somewhere.org>
            Subject: Some random subject

  You can't count on finding the address mh-users in any particular field (though it's usually somewhere on `To:`, `Cc:`, or `Sender:`). But you can bet that the out-of-band sender information, shown in the first line above, will have the address mh-users-request@ics.uci.edu -- because that's the distribution point for the mailing list. So, an entry in .maildelivery that begins with the following two arguments will catch mail from the mh-users list:

            source  mh-users-request@ics.uci.edu  ...

`default`
  Matches any message that hasn't been delivered by previous entries. For instance, look back at the Example Simple .maildelivery file: If either line 2 or line 4 delivers the message, line 6 won't. Otherwise, the message will always go into my mailbox.
`*`
  An asterisk as the first argument matches every message, whether it's been delivered by other .maildelivery entries or not.

  If the first argument has an `*` (asterisk), the action will always happen when the result (in the fourth argument) is `A` or `R`. A result of `N` or `?` can cause the entry to be skipped when those conditions are met. The Section Fourth .maildelivery Argument: Result explains the result argument.

You can do more-sophisticated matching with a program like rcvsearch.

## Second .maildelivery Argument: Pattern

The second argument, pattern, is the expression you want to match in the field selected by the field argument. For instance, line 4 of the Example Simple .maildelivery file searched for the expression "vax digest", in any combination of uppercase and lowercase, anywhere in the message `Subject:` field.

If the first argument is `default` or `*`, this second argument should be a dash (`-`).

The pattern argument is not a regular expression like the UNIX grep command uses; it's just a sequence of characters to match literally (such as the UNIX command fgrep). You can do regular expression matching with the rcvsearch program.

You can take advantage of the "substring matching" in the second argument. You might be looking for mail from root on any computer. Using `root` as a second argument will match mail from root with or without a hostname. You can also match mail from all users at a certain hostname (say, anyone at xyz.com).

The downside is that you can match some messages you weren't planning on. For instance, a search for mail from root might catch mail from Joe Grootenheimer.

One helpful trick takes advantage of the order that your .maildelivery file is read: from first entry to last. If you're trying to match more than one similar name, like joebob and joe, put the longest name first. The first entry that matches marks the message "delivered," so the entries below it won't deliver the message too:

        from  joebob  destroy  A  -
        from  joeb    qpipe    A  "/x/y/rcvstore +read_now"
        from  joe     qpipe    A  "/x/y/rcvstore +read_later"

Depending how your mail comes in, you may be able to add `@` or `!` to mark the start or end of the address. For example, to match mail from joe on a particular host and `joebob@anyhost`, use entries like the ones below. The `joebob@` will never match mail from joe on any host:

        from  joe@foo.unsd.edu   >  ?  somefile
        from  joebob@            >  ?  otherfile

Of course, that first entry would also match moejoe@foo.unsd.edu, but it's a start.

Whenever I use a destroy action (see the Section Third .maildelivery Argument: Action), I'm always very careful about what I match. The part of that Section about destroy shows a safer way with a temporary folder.

## Third .maildelivery Argument: Action

The third argument is the action: how to deliver the message. There are four different actions. Each action can be written in two ways, as a word or as a symbol. (MH 6.8 added a new mbox action, a version of the file action. It can't be written as a symbol.) The actions are:

file or `>` (right angle bracket) Action

The file or > action appends the message to a file. (That's usually a file on the mail server computer; it may not be the same computer where you do most of your work. You might write the message into a filesystem that's shared between the two computers.) Put the filename in the fifth argument. Without a pathname, the file will be in your home directory. Typical mailbox names are `/usr/mail/yourname` or `/usr/spool/mail/yourname`.

The message is appended in "mailbox format." It's separated from the previous message in the file by either:

- Two rows of CTRL-A characters. This is an MMDF-style separator:

            ...End of previous message...
            ^A^A^A^A
            ^A^A^A^A
            ...New message...

  The packf(1) command uses that format.
- A blank line followed by a standard `From` separator line. This is what the standard UNIX mail command expects:

            ...End of previous message...

            From mh-users-request@ics.uci.edu  Mon Jan 09 19:55:43 1995
            ...New message...

  The packmbox(1) command uses that format.

  In some cases, until MH 6.7.1, the slocal `From` separator didn't work right. Sometimes, a group of messages stored this way would come in as a single long message when people ran inc. If you have that problem, upgrade MH to Version 6.7.1 or later if you can. Otherwise, try using rcvstore to deliver your messages.

mbox and mmdf Actions

The MH 6.8 version of slocal added a new mbox action. It works like the file or > action except that it always uses the MMDF-style separators. There's no symbol (such as >) for mbox; you have to spell it out.

NOTE: Under nmh, mbox appends messages in mailbox format instead of MMDF format! nmh has an mmdf action that appends messages in MMDF format.

folder or `+` (plus) Action (nmh only)

This action puts a message into a folder by piping the message to rcvstore. It's only available in nmh; to do this in MH, use one of the pipe actions below.

qpipe or `^` (caret) Action

This starts a program (rcvstore, other mhook programs, or almost any UNIX program). The program reads the incoming message from its standard input. If the program succeeds and returns a zero exit status, then the action has succeeded. For example, to tell you when new mail comes in, set .maildelivery to pipe the message to rcvtty, like this:

        *   -   ^   R   /x/y/rcvtty

Here's the difference between the qpipe or `^` action and the pipe or `|` action: using the pipe or `|` action doesn't execute a program directly -- it starts a Bourne shell; the shell runs the program.

Are you using a simple command string, one that has no special characters like a semicolon (`;`) or ampersand (`&`) for a Bourne shell to interpret? Then it's more efficient to use qpipe or `^` instead of pipe or `|`. That efficiency can be helpful on busy mail server computers.

There's no command-search path set, so be sure to give the pathname for any command you list. For instance, this command probably won't work:

        *   -   ^   R   rcvtty

There are special variables that you can use in the fifth argument (the string) with the qpipe or `^` action. The easiest place to find typical settings of these variables is to run slocal -debug and look for the lines starting with `vars[n]:`, as shown in the Example Sample slocal -debug -verbose output. The variables are listed in the Table below.

Table: Variables Set for pipe and qpipe

`$(sender)` (typical value: `xandra@x.y`)
  The "return address" for the message. This is usually supplied by the system mail transfer agent. Can be set as third argument on the slocal command line. The address where you'll want to send replies, instead, is probably `$(reply-to)`.
`$(address)` (typical value: `jerry`)
  The address that was used to deliver the message to you. That's usually your username. Can be set as the first argument on the slocal command line.
`$(size)` (typical value: `12345`)
  The size of the message, in bytes.
`$(reply-to)` (typical value: `"J. Doe" <jd@x.y>`)
  The address where you should send replies. If the incoming message has a `Reply-To:` field, that address is used. Otherwise, this variable will contain the `From:` field.
`$(info)`
  Miscellaneous out-of-band information. What you get depends on your system and the situation. Can be set as the second argument on the slocal command line.

For example, to make rcvtty print the message `Mail: nnn bytes` each time new mail comes in, use:

        *,-,^,R,"/x/y/rcvtty -format \"Mail: $(size) bytes\""

The message size is filled in before rcvtty is invoked. The escaped double quotes (`\"`) group the words between them into a single argument. With the quotes, rcvtty prints a message like `Mail: 1232 bytes` on the terminal. Without them, it would print just `Mail:` and drop the rest -- that's because there has to be just a single argument after the -format option.

Single quotes (`'`) won't perform quoting inside double quotes. They will work inside double quotes with the pipe or `|` action, though.

Also watch out for variables like `$(reply-to)` that might have quotes buried in their values -- they can cause real trouble here. There's more about quoting in the pipe section below.

pipe or `|` (vertical bar) Action

The pipe or `|` action starts a Bourne shell to execute the command(s) in the fifth argument, string. The message is piped as standard input to the shell (and the command it runs). If you're running just one command, you may be able to save some execution time by using the qpipe or `^` action (explained above). But if you really need a shell, use pipe or `|`.

For example, I wanted to add a timestamp to a log file each time a message came in. I used the following entry in my .maildelivery file:

        From,uucp,|,A,"/bin/date >> uucp.msglog; /x/y/rcvstore +uucp_logs"

What is that fifth argument? The argument has two commands. The first command, `/bin/date`, appends a line with the current date and time (when a message is delivered) to the file uucp.msglog in my home directory. There's a shell semicolon (`;`) operator to separate the commands. The first command doesn't read its standard input, but the second command does -- so rcvstore reads the message that is piped to the shell. You can chain a series of commands with semicolons, as long as only one command reads the message from its standard input. (If more than one command needs to read the message, use a temporary file as shown in the Section Replacing rcvtty with Pop-Up Windows.)

The Table Variables Set for pipe and qpipe lists special variables that are set for the qpipe or `^` action. Those variables are also set for pipe and `|`. Here's an example. To send an automatic reply when I get mail from a user (sent to my system's gripe alias), I can add the following long entry to my .maildelivery file. This has two commands, separated by a semicolon:

        To,gripe,|,A,"/bin/echo \"Thanks for your $(size)-character message.  I'll handle it ASAP.  --Jerry\" | /bin/mail '$(reply-to)'; /x/y/rcvpack gripes"

- The first command runs /bin/echo (with its full pathname because echo isn't built into my Bourne shell) and pipes a reply message to the standard input of the /bin/mail command.

  The quoting (single quotes (`'`) around the `$(reply-to)` address) is very important here. Let's say my command is building a reply to a message with the following field:

            From: "Ruth A. Lee" <ruthl@fcpe.ie>

  The shell will execute the command:

            echo "Thanks ... --Jerry" | /bin/mail '"Ruth A. Lee" <ruthf@fcpe.ie>'

  In this case, the single quotes (which aren't very common in mail addresses) surrounded the double quotes (which are common) and things were fine. If the address had single quotes in it, though, the reply would have failed. What would happen if I had used double quotes (`"`) instead of single quotes, like `"$(reply-to)"`? Or worse, what if I hadn't used quotes? Part or all of the address would have been unquoted and the shell could have been very confused. For more reliable automatic replies (and simpler command strings in your .maildelivery file, too), use a separate shell script; see the Section Running Your Own Mail Handler. (For more about quoting, check UNIX Power Tools or any good shell programming book in the Reference List.)

  > NOTE: Some mail-sending commands, like /usr/ucb/mail, will break correctly-quoted addresses into single words at the spaces. Don't use commands like UCB Mail or mailx here. The versions of /bin/mail I've seen are okay. The mhmail command (in the Section Send Non-MIME Files: mhmail) is fine, too -- but it runs more slowly than /bin/mail (on my system, at least).

- The first command line didn't read standard input from the shell (/bin/mail read its stdin from /bin/echo). So the second command, rcvpack, can read the new message from its standard input -- and store it in the gripes file in my home directory.

This chapter shows lots of ways to use the pipe or `|` action; browse through the examples below.

destroy Action

(Before you read this section, take a look back at the definitions of succeeds and delivered at the start of the Section The .maildelivery File in Detail.)

The destroy action does nothing with the message, and always succeeds. This action only makes sense with the result argument A that marks the message as "delivered." (See the Section Fourth .maildelivery Argument: Result.) That's the whole point of destroy: to mark the message as delivered without actually delivering it to a file or program.

If this is the only entry in your .maildelivery file that matches the message, the text of the message will go nowhere. But if another entry has an `*` (asterisk) field, the message can still be given to a command or stored in a file.

Instead of using destroy, you might use rcvstore or rcvpack to write the message to a temporary folder or file instead. Clean out the temporary storage every so often -- use scan or msh to be sure no messages were "destroyed" that shouldn't have been. The Section Experimenting? Make Backups! has more about making backup copies of mail.

Want an example? Here's a short .maildelivery file with an example of destroy:

        From     uucp  destroy  A  -
        default  -     >        ?  /usr/spool/mail/jerry
        *        -     ^        R  /x/y/rcvtty

If the message is from uucp, the first entry matches it and "delivers" the message (to nowhere). The result argument, `A`, is important here: it means that if the destroy action succeeds (as it always does), the message is marked "delivered." The first entry works with the second entry and its `?` result argument. The second entry only writes to my system mailbox file if the message has not been delivered yet. Any message from uucp will have been delivered (to nowhere) already, so it won't go into my system mailbox.

The third entry has an `*` (asterisk) field, which matches all messages. The `R` result means that rcvtty always runs to tell me about the message, whether the message has been delivered or not. So, even though messages from uucp have been "destroyed" and aren't delivered to my system mailbox, rcvtty will notify me about them anyway.

## Fourth .maildelivery Argument: Result

The fourth argument in a .maildelivery entry is a single-character result. (As before, the definitions at the start of the Section The .maildelivery File in Detail are important.) These results are:

A Result

This result argument is probably the most common. If the field and pattern are matched, an A performs the action. If the action succeeds, the message is marked "delivered."

You'll usually use A actions toward the start of your .maildelivery file to match certain messages. Toward the bottom, you'll use ? to match messages that A didn't match (and "deliver").

R Result

Like the A result, an R will perform the action if the field and pattern on an entry match a message. But R never marks a message as "delivered."

If any entry above an R result marks the message delivered, using R can't undo that delivery. In fact, nothing can undo a "delivered" mark. The R is probably most useful at the start of .maildelivery for actions you want to run on some or all messages -- and not mark the message delivered, so that other entries can act on it.

? Result

The ? (question mark) will perform an action only if the message hasn't been delivered by some previous entry in .maildelivery. If the ? delivers a message, the message is marked "delivered."

Use ? in places where you've got a series of entries that might deliver a message -- you want the entry with the ? to deliver only if entries above didn't deliver.

N Result

The N result was added to slocal in MH 6.7.2. The N result is a little harder to explain. According to the MH 6.8.3 slocal manual page, using an N result performs the action only if both of these things are true:

- The message hasn't been delivered yet, and
- The previous action succeeded.

If this action (with an N result) succeeds, then the message is marked delivered.

That's not quite the way N works for me. I looked at the source code (the usr_delivery() function in the file uip/slocal.c) and came up with this more accurate (I hope!) description:

The N result performs the action only if:

- The message hasn't been delivered yet, and
- The previous action:
  - Succeeded, and
  - That previous action didn't have an `*` (asterisk) field.

Of course, I could have missed something. (The C code is fairly complex. It's a collection of C switch structures that fall through from case to case, several flags that test and store information about previous actions and delivery, continue commands that suddenly skip to the next .maildelivery entry, and so on. I've tried to summarize what the code does; see the Section slocal Documentation vs. Real Life.) Also, that description could change in other versions of MH. You may want to be careful if you use the N result. Test your .maildelivery file to be sure it does what you expect.

## Fifth .maildelivery Argument: String

The fifth argument, string, is a filename (for the file or `>` action) or a command line (for the qpipe or `^` and pipe or `|` actions). As for other arguments, if the filename or command line have any spaces or commas (`,`), surround the argument with double quotes (`"`).

Because there are example strings all through this chapter, I won't give another example here.

## Undocumented Arguments 6-8: select

The slocal command with MH 6.6 through 6.8.3 (and possibly in earlier versions) has an undocumented feature that lets you control actions by the time of day and whether you're logged in. (Because they're undocumented, don't depend on this feature in all MH versions.) The three undocumented arguments, which must be used together, are:

        select starttime endtime

Type the word `select` literally. `starttime` and `endtime` are two times in the format `hh:mm`, where `hh` is hours on a 24-hour clock (0 to 23) and `mm` is minutes.

If you use these optional arguments, the action will not happen if:

- The current time is between `starttime` and `endtime`, or
- You are logged on to the host that runs slocal.

So, for example:

        # if I'm not logged on and it's not between 8 AM and 5 PM,
        # send my mail to dan:
        *  -  |  R  "/x/y/rcvdist dan" select 8:00 17:00

To ignore the time restrictions, set both times to something like `0:00` (midnight); then the action will happen whenever you aren't logged in.

## Regular Expression Matching with rcvsearch

By default, the .maildelivery file matches fixed strings in the message header. For example, to match messages with bin in the `To:` field, you could use:

        to,bin,|,R,"/x/y/rcvdist operator"

But this matches any address that contains the address bin -- including, for example, robin. The rcvsearch script handles this problem and more. It does full regular expression matching on a message header or body. You run rcvsearch from a pipe or | action in the .maildelivery file. rcvsearch compares the entire message to an egrep regular expression. If egrep returns a zero exit status (meaning the search succeeded), rcvsearch runs a command like rcvdist. (rcvsearch also returns a zero status so that the pipe or | action will succeed.) Here's the previous example rewritten to match any `From:` field containing bin (and nothing else) or bin@:

        *,-,|,R,"/x/y/rcvsearch '^From: bin(@?|$)' /x/y/rcvdist operator"

Here's a way to refile messages whose bodies contain "make money" or "sex" into the spam-check folder:

        *,-,|,A,"/x/y/rcvsearch 'make money|sex' /x/y/rcvstore +spam-check"

Once you see rcvsearch, you'll see that the idea is easy to adapt to do other things. For instance, you could rewrite the program in Perl to get more searching power -- or write a small program that does a specific test. Just remember that the program will be run for every incoming message; if you make it complex, it can slow down your incoming mail processing. The Section Explanation of rcvsearch has details.