SORTMAIL(1)

πŸ“¦ version 2.4, in debian bookworm, ubuntu jammy, ubuntu noble

πŸ’Œ SORTMAIL(1) β€” General Commands Manual, but make it a group chat πŸ’Œ

okay bestie, sit down, hydrate, because we're about to learn sortmail, and honestly? it's iconic. it classifies your incoming mail. that's the whole vibe. let's get into it πŸ’…


✨ NAME ✨

sortmail β€” classify incoming mail πŸ¦‹


🌸 SYNOPSIS 🌸

sortmail [ -v ] [ -terse ] [ -home path ] [ -mailbox path ] [ -mailrc initfile ]
         [ -sortmailrc initfile ] [ -inbox filename ] [ -mbox ]
         [ -pop|pop3|pop2 user:password@host ] [ -pop|pop3|pop2 /path ]
         [ -keep ] [ -noapop ] [ var=value ] [ -dumpCrcs dbmname ]
         [ -verify ] [ -version ] username

πŸ’– DESCRIPTION πŸ’–

so. sortmail reads your email and classifies it according to patterns you specify. main character behaviour honestly. it can process incoming mail, filter mailing lists, process mail folders, or download mail from POP servers. versatile queen πŸ‘‘

if you wanna process incoming mail, you make this .forward file in your home directory:

"| /path/sortmail user"

where "/path/sortmail" is the full path where you installed sortmail, and user is your own userid. and yes, you must specify the userid, because when mail arrives, sortmail could be run as root, daemon, or literally any number of other uid's. it does not know who you are, bestie, you have to tell it 😭

once your .forward file is set up, sortmail will classify incoming mail according to the patterns in $HOME/.sortmailrc. your .sortmailrc file is like a news KILL file but lowkey more powerful. you can:

  • discard mail πŸ’€
  • have it delivered to your mailbox πŸ“¬
  • have it filed into a folder πŸ—‚οΈ
  • forward it to another address ➑️
  • or even pipe it through a shell command πŸš€

ngl, don't let the long list of options and commands below frighten you. new users might do well to skip ahead to the EXAMPLES section (coming later, iykyk), and/or read the README and sample files in /usr/share/doc/sortmail. it's giving "trust the process" ✨

when sortmail starts up, it first reads these config files, in this order:

/usr/lib/sortmailrc, /usr/local/lib/sortmailrc, /etc/sortmailrc, /usr/local/etc/sortmailrc, $HOME/.mailrc and $HOME/.sortmailrc.

periodt. πŸ“‹


πŸŽ€ OPTIONS πŸŽ€

  • -v β€” Verbose. a message gets printed to stderr or to a log file of your choosing for every single mail message. and a second -v? oh she's chatty now, a whole lot more information gets printed. double the v, double the tea β˜•
  • -terse β€” Set verbosity to 1, and omit timestamps. no timestamps, no drama, just facts.
  • -home path β€” Set the user's home directory, overriding the default taken from the user's passwd entry. you're the boss here πŸ‘‘
  • -mailbox path β€” Set the user's system mailbox, overriding the default for your system (/var/mail/user).
  • -mailrc path β€” Set the .mailrc file, overriding the default of ~/.mailrc. path may be specified as /fullpath, ~/path, ~user/path, or path. that last form is the same as ~/path, sneaky but valid.
  • -sortmailrc path β€” Set the .sortmailrc file, overriding the default of ~/.sortmailrc. path may be specified the same way as with -mailrc above. ✨
  • -inbox filename β€” Take input from the named file instead of stdin. filename may be /abspath, "~/path", "~user/path" or "+foldername". also, when the POP2 protocol is used, this option can select the inbox instead of the default mailbox. multitasking icon πŸ’…
  • -mbox β€” Indicates that the input is a standard Berkeley mailbox containing multiple messages, rather than a single message. used to process an entire mailbox at once. whole inbox, one go, slay.
  • -pop user:password@host β€” sortmail connects to the specified server, downloads mail and processes it. if password is not specified, the user is prompted interactively. ⚠️ Note: it is highly recommended that you do not specify the password on the command line on a multi-user system β€” see below, bestie, this is the part where you listen.
    • sortmail attempts to use pop3 and pop2 protocols, in that order. for obvious reasons, the user id may not contain ':'.
    • many providers assign usernames that contain '@', e.g. user@foo.com. in this case, the argument to -pop will look something like user@foo.com@pop.foo.com. yeah, double @, it's fine, it's the vibe.
    • for security reasons, sortmail attempts to hide this information from the "ps" command, but this does not work under all operating systems (e.g. Solaris). it is strongly discouraged to specify the password on the command line for this reason. 🚨
  • -pop /path β€” this variant of the -pop command reads a single line from the specified file, containing user:password@host. this is more secure than specifying the password on the command line. the file must be specified as a full pathname, starting with '/'. πŸ”
  • -pop3 user:password@host
  • -pop3 /path β€” sortmail connects to the specified pop3 server, downloads mail and processes it.
  • -pop2 user:password@host
  • -pop2 /path β€” sortmail connects to the specified pop2 server, downloads mail and processes it.
  • -keep β€” for POP use, or when -inbox and -mbox are specified. indicates that all messages are to be left in the source input box instead of being deleted. no take-backs, no deletions, we keep everything 🫢
  • -noapop β€” for POP3, do not attempt to use APOP authentication. this option is used when dealing with broken servers which do not handle APOP correctly. broken server? not our problem, we just work around it πŸ’€
  • variable=value β€” set a variable on the command line. spaces are not permitted in the string. no spaces, bestie, i'm serious.
  • -dumpCrcs dbmname β€” dump the bounce-check database in dbmname.{dir,pag} for debugging purposes. πŸ›
  • -verify β€” Verify only. examine the mailrc and sortmailrc files for errors and exit. a lil wellness check for your config 🌸
  • -version β€” Print version and exit. and if this option fails? congratulations, you have version 1.something. 😭 iconic honestly.

πŸ¦‹ SORTMAILRC FORMAT πŸ¦‹

your .sortmailrc file is a series of lines in this form:

set variable=value

/regular-expression/modifiers:command[:command...]

[ip-address]modifiers:commands..

[ip-address/mask]modifiers:commands..

[ip-address - ip-address]modifiers:commands..

(logical-expression):command[:command...]

includerc filename

listinclude filename

listexclude filename

header headerline

replace headerline

bouncecheck dbmname

where regular-expression is any ed(1)-style regular expression, modifier is any of i, t, f, s, h, a, o, and command is one of m, j, v, f file, a file, d file, +file, or | command. multiple commands may be placed on a line, separated by ':'s. and if you need to place a ':' within a command for any reason, escape it with \\. 🫣

users of rn-style KILLfiles will be familiar with this format. throwback ✨

the [ip-address] form specifies a literal IP address to be matched (e.g. 192.168.3.4) or a partial IP address (e.g. 192.168.3). this differs from a regular expression in that the '.' character must match literally, and the pattern must match at the start (e.g. 129.192.168.3 would not match the pattern given above). (note that the '[]' characters are literal here, and do not denote an optional argument. they're just brackets, bestie, that's it.)

IP addresses may also be specified as a range, e.g. "[192.168.0.0/16]" would match all IP addresses containing 192.168 in the first 16 bits. finally, IP addresses may also be specified as e.g. "[192.168.0.0 - "192.168.255.255]". so many options, so much range 🌈


πŸ’… SEARCH MODIFIERS πŸ’…

these modifiers affect how the regular expression is applied to the incoming mail. the default is 's'.

  • i β€” Ignore case. idc about your capitalization πŸ’…
  • o β€” Evaluate variables in the pattern only once, when the sortmailrc file is read. without this option, variables are evaluated every time the pattern is tested. if the pattern contains no variables, this modifier has no effect. efficiency queen πŸ‘‘
  • s β€” Test the "Subject: " line of the mail against the regular expression.
  • t β€” Test the "To: ", "Cc: " and "Apparently-To: " lines of the mail against the regular expression.
  • f β€” Test the "From: " line of the mail against the regular expression.
  • r β€” Test the Received: lines of the incoming mail against the regular expression.
  • h β€” Test the entire header of the incoming mail against the regular expression.
  • a β€” Test the entire incoming mail message against the regular expression.

any combination of s,t,f,h,a may be used. if none are specified, 's' is assumed. mix and match, it's your world 🌸


πŸ”₯ SEARCH COMMANDS πŸ”₯

these commands are executed for any message which matches a search pattern. this is where the magic happens, fr fr.

  • :m β€” Send the message to the user's mailbox. πŸ“¬
  • :m address β€” Forward the mail to the specified address. ➑️
  • :j β€” Delete the message ("junk" it.) into the void it goes πŸ’€
  • :e n β€” Set the exit code to n. when processing is complete, sortmail will exit with the given exit code instead of zero.
  • :E n β€” Exit immediately with exit code n. no goodbyes, just gone πŸšͺ
  • :k β€” Keep the message. when downloading messages from a POP server, or when -inbox and -mbox are specified, messages are normally deleted from the mailbox once transfer is successful. this option causes the message to be left behind. sentimental πŸ₯Ή
  • :f folder β€” Append the message to the given mail folder. folder may be in the formats ~/path, /abspath, ~user/path, or +name. the latter form expands to ~/folder/name where folder is the value specified for the $folder variable (default is "folders".) πŸ—‚οΈ
  • :+folder β€” Shorthand for "f +folder". lazy girl's shortcut, respect it.
  • :d file β€” Append the message to the given file in digest form. file may be in the formats ~/path, /abspath, ~user/path, or +name.
    • "Digest" form is a stripped form where each message contains only the "From:", "Subject:" and "Date:" headers, and messages are separated by a line of dashes. minimalism, we love her ✨
  • :a file β€” "Archive". identical to digest. same energy, different name.
  • :| command β€” Pipe the mail message through the given shell-command. sh(1) is used. πŸš€
  • :c β€” Continue processing. normally, sortmail applies search expressions to mail messages until a match is found. at this point, the message is dispatched and sortmail is finished. this command causes sortmail to continue comparing the message to more patterns. this option may be considered "continue", "Cc" or "copy" at your whim. thus, a message may be sent to more than one destination. multi-destination queen, we stan πŸ‘‘

more parts incoming bestie, don't go anywhere πŸ’œ

✨ COMMANDS ✨

okay bestie so here's the tea β€” these are all the commands you can drop inside a .mailrc or .sortmailrc file. yes they're giving config file energy but they're lowkey powerful πŸ’…

  • set variable = value β€” sets an environment variable, periodt. the variables sortmail actually uses are listed down below. you do not need quotes around the string, bestie. special characters like '$' or '\' can be quoted with '\'. and you can nest variables inside the value string using $name or ${name}.
heads up: set gets processed the instant the initfile is read. every other command waits around until mail messages are being read. so literally all set commands run before any other command, no matter what order they're in across your init files. iconic behavior honestly.
  • listinclude filename β€” this one's for mailing list administration (peep the section below). if you're not using sortmail to run a mailing list, just ignore it, no thoughts head empty. it points at a file full of addresses that are allowed to post to the list. every line holds one regular expression representing a list member's email address. lines formatted like
User Name <address>

only use the address part of the line β€” which means the mailing list itself can be used as the include list, slay. you can stack multiple include lists with multiple listinclude commands. if you use listinclude, posters have to show up in at least one of the lists. if there are no include files at all? anybody can post. rejected messages get handled per the $reject variable, which must exist and holds either a filename or a search command (see below). for more on include and exclude files, go read MAILING LISTS further down.

  • listexclude filename β€” the opposite vibe: a list of addresses that are not allowed to post. same format as listinclude. to post, a user must be in the listinclude file(s) (if any exist) and not in the listexclude file(s).
  • replace Header: value β€” for mailing list processing. header lines matching the specified header get swapped out for the new value. if nothing matches, the line just gets appended to the message header instead. typically used to change the "From: " header so it points at the list address instead of whoever actually sent it, and to set an "Errors-To: " header. if the value field is empty, the specified header line gets deleted. gone. bye.
  • header Header: value β€” same format as replace, except header lines are always appended, never replaced. typically used to add comments.
  • bouncecheck dbmname β€” the last-resort mechanism for catching mail loops. a crc-32 hash of the text portion of the message gets computed and stashed in dbmname.{dir,pag}. if another message with the same hash code shows up within the next 60 days, that message gets rejected and disposed of exactly as described in $reject.
ngl this is not a perfect mechanism. broken mailers might decide to add some comments to the message before bouncing it back. 😭
  • includerc filename β€” process commands from the named file. and yes bestie, include files can be nested. it's giving inception. πŸŒ€

πŸ’– VARIABLES πŸ’–

variables show up in the form $name or ${name}, and they can appear anywhere in the init file. sortmail uses the following ones, and you can change any of them in your .mailrc or .sortmailrc files. you can also set them right on the command line, no cap.

  • default β€” the command(s) used to handle unclassifiable mail. default behavior is to dump unclassifiable mail into mailbox. another reasonable value might be "+other".
  • delay β€” used to stop runaway mail loops, which is especially useful for mailing list administration. specifies a delay in seconds imposed before mail is forwarded to another address or piped through a command. for example, a 600-second delay (ten minutes) would cap a mail loop at one message every ten minutes. πŸ•
  • folder β€” the user's mail folders directory. mail folders are denoted by a leading '+' in their name, and they live in ~user/$folder/. default is "folders". a lot of people set this to "Mail" instead.
  • from β€” for mailing list administration. specifies the value of the "From " line when mail is forwarded to the list. not every version of sendmail will honor this. you might need to make sortmail suid-uucp, or add your username to the "trusted users" entry in /etc/sendmail.cf.
  • HOME β€” the user's home directory. used to find initialization files and the user's folders directory. default is ~user.
  • lines β€” count of lines of text in the message. set by sortmail and updated for every message.
  • logfile β€” debugging messages get sent to the specified log file. if sortmail is executed from the command line, the default is stderr. otherwise the default is /tmp/sortmail.log.

if logfile can't be opened, stderr gets used instead. note that when sortmail is used from your .forward file to filter incoming messages, anything sent to stderr gets bounced straight back to the sender. πŸ™ˆ

  • mailbox β€” the user's mail box. default is /var/mail/user.
  • mailrc β€” full path of the user's .mailrc file. there's genuinely no real point in changing this one, bestie.
  • maxlines β€” maximum number of message lines that get searched during pattern matching. restricting this can make searches quicker and keep /tmp from filling up. default value is 5000.
  • reject β€” for mailing list administration. specifies the file or command that rejected mail gets sent to.
  • sendmail β€” the command used to deliver mail. default value is "/usr/lib/sendmail -om -oi". if '%f' shows up in the string, it gets expanded to the temporary file name holding the message. otherwise the message is delivered to the command's standard input. the special values "SMTP" and "SMTP hostname" cause the message to be delivered directly to an SMTP port. ✨
  • size β€” size of the message in bytes. set by sortmail and updated for every message.
  • sortmailrc β€” full path of the user's .sortmailrc file. again, no real point in changing it.
  • timeout β€” timeout in seconds for POP connections.
  • user β€” the user on behalf of whom sortmail is running. this value must be specified on the command line when sortmail is executed from a .forward file, but it may be changed later. it's used to determine the user's home directory, among other things.
  • vacation β€” if set, mail to user will also be piped through vacation(1).
  • TMPDIR β€” directory used for temporary files. default is "/tmp".

and on top of all that, sortmail defines these environment variables before handing a message off to another program:

  • FROM β€” the sender of the message
  • SUBJECT β€” the subject line from the incoming message

🌈 EXAMPLES 🌈

here's a sample .sortmailrc file, bestie. feast your eyes:

set default=+other
/MAILER-DAEMON/f:+bounces
/falk/t:m
/bldg8/t:m
/joe/f:m:c
/for brenda/s:k
/scubaclub/t:+scuba
/scuba/s:+scuba
/marko/f:j
/testing/t:m falk@lab
/jym@apple/f:| /home/falk/bin/fixjim
/^Precedence: junk/h:+other
(/bill/f && /dive/s):+scuba
[211.114.0.0/16]r:j

so in this example, the folder directory and the other variables have whatever values were set in .mailrc. unclassifiable mail gets sent to the folder "+other". mail from "MAILER-DAEMON" goes to the folder "+bounces". and mail to "falk" or "bldg8" goes straight to my mailbox.

mail from my friend joe is sent directly to my mailbox, and processing keeps going to see if there's somewhere else it should go too.

mail labeled "for brenda" is left at the POP server untouched. (this only works if the email is being downloaded from a POP server β€” otherwise it would just be lost. 😭)

mail to the scuba club, or with "scuba" in the subject line, gets sent to the "+scuba" folder. mail from marko is thrown away unread. mail to the "testing" alias is forwarded to my account on another machine.

mail from my friend jym, who formats his mail in a funny way, is passed through a shell script that cleans up his messages and appends them to my mailbox. mail messages with "^Precedence: junk" anywhere in the header get filed in +other.

the next-to-last line shows off a feature that's brand new in sortmail version 2: logical expressions. in this case, mail from bill with the subject "dive" gets added to the scuba folder. logical expressions get described in detail below. 🧠

finally, the last line shows another feature that's new to sortmail version 2: IP ranges. in this case, all email with an IP address in the "211.114.0.0/16" range inside a "Received:" line gets junked unread. πŸ’€

okay so important: the patterns get applied in the order given. it genuinely matters, for example, that the "MAILER-DAEMON" pattern comes before the "falk" pattern, so that mail from MAILER-DAEMON is filed in +bounces even if it's directed to me personally. same energy with marko β€” mail from marko will not get junked if it's addressed to me personally. order is everything, periodt.

🧠✨ LOGICAL EXPRESSIONS ✨🧠

okay bestie this is the fun part πŸ’… logical expressions let you write actually complicated rules for dealing with your mail. like, say, you want every message from a certain domain that's bigger than a certain size to get deleted unread β€” unless a specific keyword shows up in the header. yeah. we can do that. 🫢

logical expressions are built out of these operators, listed here in order of precedence (top = binds tightest, bottom = loosest):

  • n β€” integer constant
  • $var β€” variable
  • /pattern/ β€” regular expression. Evaluates as 0 or 1.
  • ! β€” logical not
  • * β€” multiply
  • / β€” divide
  • + β€” add
  • - β€” subtract
  • < β€” less than
  • <= β€” less than or equal
  • > β€” greater than
  • >= β€” greater than or equal
  • == β€” equal
  • != β€” not equal
  • & β€” logical AND
  • && β€” logical AND
  • | β€” logical OR
  • || β€” logical OR
  • , β€” comma

and if the default precedence isn't giving what you want? parenthesis exist, use them, they modify the order of evaluation. periodt. 🌈

also: ':' commands may follow any close-parenthesis or regular expression. examples down below, keep reading πŸ‘€


πŸŽ€ the two kinds of AND and OR (this part matters)

the second form of logical AND and OR β€” that's "&&" and "||" β€” is optimized like this: if the left half of an AND is false, or the left half of an OR is true, then the right hand side does not get evaluated at all. so the move is: put a simple expression (like a subject match, cheap) on the left, and a complex expression (like a message body search, expensive) on the right. if the simple one already decides it β€” false for AND, true for OR β€” the complex one never even gets tested. 🧚

the first form β€” "&" and "|" β€” always tests both sides of the expression. no shortcuts, no skipping. πŸ’€


πŸ“ the comma operator, explained for the C-innocents

ngl the comma deserves a little yapping for anyone who hasn't met it in C. the comma operator evaluates the expressions on both sides and returns the expression on the right β€” just straight up ignoring the one on the left. so "3 , 4" evaluates as 4. the comma operator is useful only when the expression on the left has some sort of side effect when evaluated β€” i.e. it contains ':' commands. that's the whole bit. πŸ’–


🌸 sample expressions (the vibes, demonstrated)

(/joe/f):m

mail from joe comes directly to me. this is the same as /joe/f:m. same thing, fewer parentheses, still iconic. ✨

(/joe/f && /dive/s):+scuba

mail from joe with "dive" in the subject line goes to the scuba folder. πŸ¦‹

(/joe/f && !/dive/s):m
/joe/f:+scuba

mail from joe without "dive" in the subject line goes directly to me. else, mail from joe goes to the scuba folder. 🫢

(/joe/f && $lines > 1000):j

looks like joe posted another long boring vacation report to the scuba list. junk it. πŸ’€

(/earthlink/r && $size > 32768 &&
     !(/key west/ia || /caymans/ia) ):j

junk it if it came from or passed through earthlink (as shown by the Received: lines), and the size is greater than 32k and it does not contain the phrase "key west" or "caymans" anywhere in the message body. case is ignored in the body search. note that we examine the message body last to avoid downloading the message unnecessarily. efficiency queen behavior πŸ‘‘

note also that logical expressions may be continued across multiple lines as needed. no cap, just hit enter and keep going. ✨

(1):+maillog:c

an extremely simple expression. (1) is always true, so all mail that reaches this expression is filed to the folder "maillog". the ":c" command causes processing to continue.

this expression is a very good one to have at the top of your .sortmailrc when testing a new configuration. all incoming mail is copied to a backup log before more complex expressions are tested. πŸ›Ÿ do this. trust.
(/joe/f:+joemail && /scuba/:+scuba)

this example shows the use of ':' commands within an expression. mail from joe goes to the "joemail" folder. if it also contains the subject "scuba", it goes to the scuba folder as well. 🌈

(/joe/f:+joemail , /scuba/:+scuba)

this example shows the use of the ',' operator. mail from joe goes to the joemail folder. whether or not this matches, the mail is tested again to see if it belongs in the scuba folder. if so, then processing is finished. πŸŽ€

(/sex/:+sex && /drugs/:+drugs && /rock-n-roll/:+rock):+bacchanalia

okay so this pattern does not do what it looks like it was intended to do. πŸ’€ at first glance it looks as if the pattern is intended to place all messages containing "sex" in the subject into the sex folder, all "drugs" messages into the drugs folder, all "rock-n-roll" messages into the rock folder and place messages into the bacchanalia folder if they match on all three keys. however β€” logical expressions are only evaluated as far as necessary. if the "sex" pattern is not matched, the next two will not be tested at all. a "rock-n-roll" message would be missed by this pattern. 😭

in this case, the '&' operator should be used instead of '&&'. that's the fix, bestie. 🫢

πŸ’Œ MAILING LISTS πŸ’Œ

Skip this section unless you're using sortmail to administer a mailing list. Like, genuinely skip it, bestie, it's not a vibe check for the casual user. 🫢

In a homogeneous environment, it is usually not necessary to use sortmail or any other mail filter. You would simply create the alias in /etc/aliases and let sendmail(8) handle everything. Easy. Slay. ✨

However, in a heterogeneous environment, there can be problems. The internet document Rfc822 specifies the handling of internet mail, but there are many mailers out there which do not honor Rfc822 and cause trouble. Not surprisingly, many of the major service providers are among the worst troublemakers. 😭 Not naming names but… we all know.

What typically happens is that for some reason, some member of your mailing list suddenly cannot receive mail. The service provider at the user's end bounces an error message back to the list itself rather than to the original sender or the administrator. The error message is then resent to the list subscribers β€” including the the one who cannot receive mail, causing another bounce. This creates a loop, sending and resending bounce messages to everybody on the loop every few minutes. πŸ’€ And Murphy's Law states that this will happen while you are on vacation. Obviously. It's giving cursed.

Here is how to administer a mailing list:

First, (as root) edit /etc/aliases and add the following lines:

scubaclub: "| /usr/yourname/sortmail -sortmailrc scubaclubrc
yourname"
scubaclub-real: :include:/usr/yourname/scubalist
scubaclub-request: yourname
owner-scubaclub: yourname

The first entry indicates that mail to the scuba club goes through sortmail, using a specific sortmailrc file. The second entry is the actual scuba club alias to which sortmail will forward the mail. The third entry is a standard list address which will be used by users to contact you directly; this should always exist for any mailing list. The final entry is used by the sendmail system to send internal errors back to you.

(Most unix systems require you to run newaliases(8) after editing /etc/aliases.) Don't forget this one, fr fr, it's the classic oopsie. πŸ™ˆ

Second, create /usr/yourname/scubalist, containing the names and addresses of everybody in the list.

yourname  <youraddress>
Joe Shmoe <joe@foo.com>
Jane Doe  <jane@bar.com>
(etc.)

Third, create a sortmailrc file which will be used to filter incoming mail. πŸ‘‘

# general variables

set alias = scubaclub
set owner = yourname
set site = yourhost.com
set digestDir = ~/Maillists/Scubaclub

# mail that makes it through the filter gets mailed to
# the list and archived.
set default = m $alias-real@$site:a $digestDir/archive

# mail that gets rejected is mailed to me
set reject = m $owner

# catch anything that looks like a bounce or a loop

/Mailer-Daemon/f:m $owner
/MAILER-DAEMON/f:m $owner
/Postmaster/f:m $owner
/scubaclub/f:m $owner
/X-List-Name: scubaclub/h:m $owner
bouncecheck $digestDir/bounceDb

# (For some reason, we can't set Errors-To to $owner@$site,
# because if we do, sendmail will expand $owner into an
# invalid value before connecting to SMTP.  It would probably
# be ok if I didn't have a personal .forward file.  By adding
# a '\\' to the address, we avoid the problem.)

replace Reply-To: $alias@$site
replace Errors-To: \\$owner@$site
header Comment: send add/delete requests to $alias-request@$site
header X-List-Name: $alias

In this example, the variables $alias, $owner, $site and $digestDir are not used internally by sortmail, but are created for convenience and generality. Cute little helpers, that's all. πŸ’…

The search patterns are used to detect possible mail loops, and as such, always send mail to the owner. Mail that makes it without matching any of the patterns is sent to the list.

As a last resort, the mail is processed by the bouncecheck command which maintains a database of previously-seen messages and will reject any message that seems to be a repeat.

If mail passes through all the patterns unmatched, it is probably a valid message. In this case, the message is processed by the commands in $other, which mail the message to the actual alias, and append a digest version of the message to ~/Maillists/Scubaclub/archive.

Finally, the headers of outgoing mail are modified. The "Reply-To:" header is added so that replies to mail from the list are sent to the list at large, and not just to the sender of the original message. The "Errors-To:" header is added so that bounces will be sent to the administrator instead of to the list in general. (Not all mail transfer agents honor the "Errors-To:" header.) 🫠

The "X-List-Name:" header line serves two purposes. First, it lets recipients know what they're receiving. Secondly, it is a trick used to help detect bounces. It is added so that it may be searched for in incoming mail. If an incoming message contains this header, it is likely that this is a bounce, and is sent to the administrator for inspection.


If some of your list members wish to receive messages in "digest" form, you can split the list into two sections, one normal and one for the members who want digests. Add the following line to /etc/aliases:

scubaclub-digest: :include:/usr/yourname/scubadigest

and change $default in /usr/yourname/scubaclubrc:

set default = m $alias-real@foo:a $digestDir/archive:d $digestDir/digest

Now, incoming messages will be copied to ~/Maillists/Scubaclub/digest as well as to ~/Maillists/Scubaclub/archive. On a nightly basis, execute a program that will test to see if ~/Maillists/Scubaclub/digest is non-empty, and if so, mail it to scubaclub-digest@yourhost and empty it. πŸŒ™βœ¨

#! /bin/sh
#
# collect the digest file, prepend some header info and transmit

digest=/usr/yourname/Maillists/Scubaclub/digest
alias=scubaclub
host=yourhost.com

if [ ! -s $digest ] ; then
  exit 0
fi

cat $digest | awk "
BEGIN {
  print \"Return-Path: $alias@$host\"
  print \"Date: `date`\"
  print \"From: $alias@$host\"
  print \"To: $alias@$host\"
  print \"Subject: $alias digest\"
  print \"X-List-Name: $alias\"
  print \"\"
  print \"\"
}
{print}" | \
/usr/lib/sendmail -om -oi -f$alias@$host $alias-digest

rm $digest
touch $digest
chmod a+w $digest

πŸ“ NOTES

Remember that sortmail can be executed under any userid (e.g. root, daemon or the sender of the mail) depending on who sent the mail, and whether or not it came from the local machine. Because of this, you cannot depend on any user environment to be available, especially environment variables and path. All filenames and program names should be specified as full paths, except that "~", "~user" and "+folder" forms are understood. The permissions of sortmail and every directory along its path should be such that any user can execute it. πŸ’…

If you pipe incoming mail through a program, that program should not generate any output to stdout or stderr whatsoever. If it does, that output will be sent back to the originator of the mail as if the mail had bounced. Yeah. Not the vibe. πŸ’€

Always test your setup thoroughly, especially when administering mail lists. Mistakes usually result in bounce messages being sent to the originator of mail. This can be catastrophic with a mail list. When testing a mail list, start with a test list containing only your name and a known bad address to test bounce handling. Use of a logfile and -v is recommended for the first few days after installing. πŸ§ͺ✨

The bounce detection mechanism tries to be robust, but as the saying goes, you can never make a system that's foolproof because some fools are ingenious. No matter how thorough the detection mechanism is, there is a broken mailer out there somewhere that can defeat it. 😭 It's giving whack-a-mole.

Never leave a mailing list unattended; that's when bounce loops always seem to start. If you go on vacation, either temporarily shut the list down, or designate someone who can turn it off in an emergency. πŸ‘‘

πŸšͺ EXIT STATUS

The following exit values are returned:

  • 0 β€” Normal exit, mail delivered successfully. πŸŽ‰
  • 1 β€” Normal exit, but no messages waiting (-mbox and -pop options)
  • 2 β€” User error in command line options.
  • 3 β€” Error in .mailrc or .sortmailrc file.
  • 4 β€” Out of memory
  • 5 β€” Cannot determine user name
  • 6 β€” Cannot determine user directory
  • 7 β€” Cannot open a temporary file. In this case, sortmail attempts to recover by writing the incoming mail to the user's inbox.
  • 8 β€” Cannot open bounce-check database (-dumpCrcs command)
  • 9 β€” Cannot open input file (-inbox option).

πŸ› BUGS

If multiple instances of sortmail are executing and printing status messages to the same logfile, the output may become jumbled. πŸ™ƒ Chaos, honestly.

The maxlines variable is not yet implemented. It's on the vision board bestie, but it's not real yet. πŸŽ€

πŸ‘€ SEE ALSO

Mail(1), procmail(1), fetchmail(1).

Copyright 1990, 1999 by Edward A. Falk (falk@efalk.org)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

regex.[ch] is covered by the GNU copyleft.


And that's the whole page, bestie!! πŸŽ€πŸ’– This one's from the SORTMAIL(1) manual, dated 21 Apr 1991 β€” yes, ancient, yes, still iconic, no cap. Thanks for reading all four parts, you absolute main character. mwah πŸ’…βœ¨

πŸ“¦ where this page comes from