S-NAIL(1)
๐ S-NAIL(1) ๐
Heyyy bestie, welcome to the S-nail manual, the General Commands section, aka section 1 ๐ Let's get into it.
โจ NAME โจ
Okay so the whole NAME is S-nail [v14.9.25] โ send and receive Internet mail ๐ฌ It's literally a mail client for sending and receiving Internet mail, and it's giving main character energy ๐
๐ฆ SYNOPSIS ๐ฆ
Here's how you can call it. There are three main ways, plus the two lil help flags ๐ง
s-nail [-DdEFinv~#] [-: spec] [-A account] [:-a attachment:]
[:-b bcc-addr:] [:-C "field: body":] [:-c cc-addr:]
[-M type | -m file | -q file | -t] [-r from-addr]
[:-S var[=value]:] [-s subject] [:-T "field: addr":] [:-X cmd:]
[:-Y cmd:] [-.] :to-addr: [-- :mta-option:]
s-nail [-DdEeHiNnRv~#] [-: spec] [-A account] [:-C "field: body":]
[-L spec] [-r from-addr] [:-S var[=value]:] [-u user] [:-X cmd:]
[:-Y cmd:] [-- :mta-option:]
s-nail [-DdEeHiNnRv~#] [-: spec] [-A account] [:-C "field: body":] -f
[-L spec] [-r from-addr] [:-S var[=value]:] [:-X cmd:] [:-Y cmd:]
[file] [-- :mta-option:]
s-nail -h | --help
s-nail -V | --version
๐ DESCRIPTION ๐
Okay, big news first ๐ S-nail (S-nail) is about to get major changes in v15.0 (circa 2022). Some backward incompatibilities can't be avoided, sorry bestie ๐ญ The โCOMMANDSโ are changing to โShell-style argument quotingโ, and shell metacharacters are going to become (more) meaningful. Some commands already accept new syntax today via wysh (โCommand modifiersโ). Behaviour gets flagged as [v15-compat] and [no v15-compat]. Setting v15-compat (โINTERNAL VARIABLESโ) will pick the new behaviour when it applies, and giving it a value makes wysh an implied default. Anything marked [Obsolete] is on its way out ๐ฅ
Warning! v15-compat (with value) will be a default in v14.10.0! ๐จ
S-nail gives you a simple and friendly space for sending and receiving mail ๐ธ It's meant to do everything the POSIX mailx(1) command does, but it's MIME capable, and it optionally adds extras like line editing, S/MIME, SMTP and POP3, among others. It splits your incoming mail into its individual messages so you can deal with them in any order you want ๐ฆ It has loads of โCOMMANDSโ and โINTERNAL VARIABLESโ for managing messages and sending mail. There's also simple editing to help you compose outgoing messages, plus steadily more powerful and reliable non-interactive scripting ๐ง
๐ Options ๐
-: spec,--resource-files=..โ Controls which โResource filesโ get loaded (as viasource) ๐specis parsed case-insensitively. The lettersmeans the system wides-nail.rc, andumeans your personal~/.mailrc. The original system wide resource is compiled in too, and you can reach it withx.-and/turn resource files off. Order matters, and the default issu. This option overrides-n.-A name,--account=..โ Activates the user accountnameonce startup is finished (resource files loaded, and only-Xcommands get run) โจ It then switches to its โprimary system mailboxโ (most likely the inbox). If activation fails, the program exits when it's used non-interactively, or if eithererrexitorposixis set.-a file[=input-charset[#output-charset]],--attach=..โ (Send mode) Attachesfile๐ For (Compose mode) options, check out~@and~^.filegets tilde expansion (see โFilename transformationsโ andfolder). If it isn't accessible but has a=in it, then anything before the last=is used as the filename, and anything after it is used as a character set specification, as shown.
If you only specify an input character set, the input side is fixed and no character set conversion happens. An empty value or the special string - gets taken as ttycharset (the default). If you also specify an output character set, the conversion you asked for happens right away, no matter the file type and content. The exception is an empty string or -, which picks the default conversion algorithm (see โCharacter setsโ). In that case there's no immediate conversion, and file plus its contents get MIME-classified first (โHTML mail and MIME attachments, The mime.types filesโ). Only the latter mode is available unless features includes ,+iconv, ๐
-Bโ ([Obsolete]: S-nail always uses line-buffered output ๐ฅ If you want line-buffered input even in batch mode, enable batch mode with-#.)-b addr,--bcc=..โ (Send mode) Sends a blind carbon copy to the recipientaddr๐ You can use this more than once. Also see the section โOn sending mail, and non-interactive modeโ.-C "field: body",--custom-header=..โ Creates a custom header that sticks around for the whole session ๐ A custom header is the field name, then a colon:, then the field content body. For example:-C "Blah: Neminem laede; imo omnes, quantum potes, juva". Standard header field names can't be overwritten by custom headers. Runtime adjustable custom headers live in the variablecustomhdr. In (Compose mode),~^(one of the โCOMMAND ESCAPESโ) as well asdigmsgare the most flexible and powerful ways to manage message headers ๐ You can use this option more than once.-c addr,--cc=..โ (Send mode) Just like-b, except it puts the argument in the list of carbon copies ๐-D,--disconnectedโ [Option] Starts up withdisconnectedset ๐-d,--debugโ Enters a debug-only sandbox mode by setting the internal variabledebug๐งช You can get the same thing with-S debugorset debug. Also see-v.-E,--discard-empty-messagesโ (Send mode) Setsskipemptybody, so messages with an empty message part body get discarded (successfully, bestie) ๐๏ธ-e,--check-and-exitโ Just checks whether mail is there (in the system inbox, or the one you picked with-f) ๐ If yes, it returns an exit status of zero. If not, it returns a non-zero value. To narrow down which mails count in this check, add a message specification with-L. Quickrun: it doesn't open an interactive session.-Fโ (Send mode) Saves the message you're sending into a file named after the local part of the first recipient's address, instead of intorecord๐-f,--fileโ Reads the contents of your โsecondary mailboxโMBOX(or the file you specify) so it can be processed ๐ฌ When you quit S-nail, it writes your undeleted messages back to that file (but be aware of theholdoption ๐). The optionalfileargument goes through some special โFilename transformationsโ (as viafolder). Note thatfileisn't an argument to the flag-f. It's taken from the command line after option processing is finished. If you want to use a file that starts with a hyphen-minus, prefix it with a relative path, like./-hyphenbox.mbox๐ง-H,--header-summaryโ Displays a summary of headers for the given folder (depending on-u, the inbox orMAIL, or whatever you set with-f), then exits ๐ A configurable summary view is available via-L. This mode doesn't honourshowlast. Quickrun: it doesn't open an interactive session.-h,--helpโ Shows a brief usage summary ๐ซถ Use--long-helpfor a list of long options.-iโ Setsignoreso tty interrupt signals get ignored ๐ โโ๏ธ-L spec,--search=..โ Displays a summary of headers for every message that matches the givenspec, in the folder found by the same algorithm-Huses, then exits ๐ See the section โSpecifying messagesโ for the format ofspec. This mode doesn't honourshowlast.
If you also give -e, no header summary gets produced. Instead, S-nail tells you through its exit status whether spec matched any messages (0) or none (1). Any verbose output is suppressed in this mode, so if you want it, you have to turn it on explicitly (see -v). Quickrun: it doesn't open an interactive session.
-M typeโ (Send mode) Flags standard input with the MIMEContent-Type:set to the known type you give (โHTML mail and MIME attachments, The mime.types filesโ), and uses it as the main message body ๐ [v15 behaviour may differ] Using this option bypasses processing ofmessage-inject-headandmessage-inject-tail. Also see-q,-m,-t.-m fileโ (Send mode) MIME classifies the file you specify and uses it as the main message body ๐ [v15 behaviour may differ] Using this option bypasses processing ofmessage-inject-headandmessage-inject-tail. Also see-q,-M,-t.-N,--no-header-summaryโ Stops the initial display of message headers when you read mail or edit a mailbox folder, by callingunseton the internal variableheader๐-nโ Standard flag that stops the system wides-nail.rcfrom being read at startup ๐ The-:option gives you more control over the startup sequence. Also see โResource filesโ.-q file,--quote-file=..โ (Send mode) Starts the message body off with the contents offile๐filecan be standard input-, but only in a non-interactive context. Also see-M,-m,-t.-R,--read-onlyโ Any mailbox folder (aka folder) you open will be in read-only mode ๐๐-r from-addr,--from-address=..โ The RFC 5321 reverse-path is what's used for relaying and delegating messages to their destination(s), like when reporting delivery errors ๐ฎ It's normally derived from the address in thefromheader (or, if that has multiple addresses, fromsender). A file-based aka local executablemta(Mail-Transfer-Agent) instead uses the local identity of the user who starts things up.
When you use this one, the single addressee from-addr gets assigned to the internal variable from ๐. But that's not all, bestie! -f from-addr also gets passed along to a file-based mta every single time a message goes out ๐. And if from-addr includes a user name, the address components get split up and the name part is handed to the file-based mta on its own via -F name ๐. Even though it isn't even a recipient, the shquote expandaddr flag is still supported ๐
.
If you pass an empty string as from-addr, the content of the variable from (or, if that holds multiple addresses, sender) gets evaluated and used for this purpose whenever the file-based mta is contacted ๐ง. By default, without -r that is, neither -f nor -F command line options get used when contacting a file-based MTA, unless this automatic deduction is enforced by setting the internal variable r-option-implicit ๐ฆ.
๐ Remarks: okay this part is serious. Many default installations and sites won't let you override the local user identity like this unless either the MTA has been configured accordingly or the user is a member of a group with special privileges. And passing an invalid address will cause an error.
-S var[=value],--set=..โ Set (or, with the prefix stringno, as documented in "INTERNAL VARIABLES", unset) a variable and optionally assign it a value, if supported ๐ฅ. [v15 behaviour may differ] The whole expression is evaluated as if it were written within dollar-single-quotes (see "Shell-style argument quoting") if the internal variablev15-compatis set. If the operation fails, the program will exit if any oferrexitorposixare set. Settings made via-Scan't be changed from within "Resource files" or from an account switch initiated by-A. They become mutable again before commands registered via-Xare executed ๐.-s subject,--subject=..โ (Send mode) Sets the subject of the message you're sending. Newline (NL) and carriage-return (CR) bytes are invalid and will be normalized to space (SP) characters ๐.-T "field: addr",--target=..โ (Send mode) Addsaddrto the list of receivers targeted byfield. For now the only supported fields arebcc,cc,fcc, andto๐ฏ. The field and the body (address) are separated by a colon:and optionally some blank (space, tabulator) characters. Theshquoteexpandaddr flag is supported.addrgets parsed like a message header address line, as if it were part of a template message fed in via-t, and the same modifier suffix is supported too. This option may be used multiple times ๐ฆ.-t,--templateโ (Send mode) The text message given on standard input is expected to contain one or multiple plain text message headers, separated from the message body by an empty line ๐. [v15 behaviour may differ] Readily prepared MIME mail messages can't be passed, sorry bestie ๐. Headers can span multiple consecutive lines if the follow lines start with any amount of whitespace. A line starting with the number sign#in the first column is ignored ๐ธ. Message recipients can be given via the message headersTo:,Cc:,Bcc:(the?singlemodifier enforces treatment as a single addressee, for exampleTo?single: exa, <m@ple>) orFcc:. They'll be added to any recipients specified on the command line, and they're likewise subject to expandaddr validity checks. If a message subject is specified viaSubject:, that one gets used in favour of the one given on the command line โจ.
More optional headers are Reply-To: (possibly overriding reply-to), Sender: (sender), and From: (from and / or option -r). Message-ID:, In-Reply-To:, References: and Mail-Followup-To: are, by default, created automatically depending on message context, and they'll be used if you specify them (though a special address massage will still occur for the latter) ๐ง. Any other custom header field (also see -C, customhdr and ~^) is passed through entirely unchanged. In conjunction with the options -~ or -# it's possible to embed "COMMAND ESCAPES" ๐. Also see -M, -m, -q.
-u user,--inbox-of=..โ Initially reads the "primary system mailbox" ofuser, appropriate privileges presumed. This is effectively identical to-f %user๐ฌ.-V,--versionโ Shows the S-nails version and exits ๐. Theversioncommand also shows the list of features:$ s-nail -:/ -Xversion -Xx.-v,--verboseโ Sets the internal variableverboseto enable logging of informational context messages. Using it multiple times increases the level of verbosity ๐. Also see-d.-X cmd,--startup-cmd=..โ Adds the givencmd(or multiple, for a multiline argument) to a list of commands to be executed before normal operation starts โญ. The commands get evaluated as a unit, just as viasource. Correlates with-#anderrexit.-Y cmd,--cmd=..โ Adds the givencmd(or multiple, for a multiline argument) to a list of commands to be executed after normal operation has started ๐. The commands get evaluated successively in the given order, as if they were given on the program's standard input. That means before interactive prompting begins in interactive mode, and after standard input has been consumed otherwise ๐ .-~,--enable-cmd-escapesโ Enables "COMMAND ESCAPES" in (Compose mode) even in non-interactive use cases ๐ซถ. This can, for example, be used to automatically format the composed message text before sending the message:
$ ( echo 'line one. Word. Word2.';\
echo '~| /usr/bin/fmt -tuw66' ) |\
LC_ALL=C s-nail -d~:/ -Sttycharset=utf-8 bob@exam.ple
-#,--batch-modeโ Enables batch mode, and honestly it's giving automation ๐ค. Standard input is made line buffered, the complete set of (interactive) commands is available, and processing of "COMMAND ESCAPES" is enabled in "Compose mode". Diverse "INTERNAL VARIABLES" get adjusted for batch necessities, exactly as if done via-S:emptystart,noerrexit,noheader,noposix,quiet,sendwait,typescript-mode, as well asMAIL,MBOXandinbox(the latter three to/dev/null). Also, the values ofCOLUMNSandLINESare looked up and acted upon ๐. The following prepares an email message in a batched dry run:
$ for name in bob alice@exam.ple lisa@exam.ple; do
printf 'mail %s\n~s ubject\nText\n~.\n' "${name}"
done |
LC_ALL=C s-nail -#:x -Smta=test \
-X'alias bob bob@exam.ple'
-.,--end-optionsโ This flag forces termination of option processing in order to prevent "option injection" (attacks) ๐ก๏ธ. It also forcefully puts S-nail into send mode, see "On sending mail, and non-interactive mode" ๐ซก.
If the setting of expandargv allows their recognition, all mta-option arguments given at the end of the command line after a -- separator will be passed through to a file-based mta (Mail-Transfer-Agent) and persist for the entire session ๐ง. expandargv constraints do not apply to the content of mta-arguments. Command line receiver address handling supports the shquote constraint of expandaddr. For more, please see "On sending mail, and non-interactive mode" ๐ธ.
$ s-nail -#:/ -X 'addrcodec enc Hey, ho <silver@go>' -Xx
๐ธ A starter ๐ธ
S-nail is a direct descendant of BSD Mail, which itself is a successor to the Research Unix mail that "was there from the start" according to "HISTORY" ๐. It thus represents the user side of the Unix mail system. The system side (Mail-Transfer-Agent, MTA) was traditionally taken by sendmail(8), and most MTAs provide a binary of this name for compatibility reasons ๐
. If the [Option]al SMTP mta is included in the features of S-nail, then the system side isn't a mandatory precondition for mail delivery ๐ฆ.
S-nail strives for compliance with the POSIX mailx(1) standard, but posix, one of the "INTERNAL VARIABLES", or its "ENVIRONMENT"al equivalent POSIXLY_CORRECT, needs to be set to adjust behaviour to be almost on par. Almost, because there's one important difference: POSIX "Shell-style argument quoting" is ([v15 behaviour may differ] increasingly) used instead of the "Old-style argument quoting" that the standard documents, which is believed to be a feature ๐. The builtin as well as the (default) global s-nail.rc "Resource files" already bend the standard-imposed settings a bit ๐.
For example, hold and keepsave are set in order to suppress the automatic moving of messages to the "secondary mailbox" MBOX that would otherwise occur (see "Message states"). keep is set to not remove empty system MBOX mailbox files (or all empty such files in posix mode), so file permissions don't get mangled when files eventually get recreated ๐งน.
To enter interactive mode even if the initial mailbox is empty, emptystart is set. editheaders allows editing of headers, and fullnames stops addresses from being stripped down in "Compose mode" ๐. quote includes the message being responded to when replying, and it's indented by an indentprefix, which also deviates from the standard-imposed settings โจ. mime-counter-evidence is fully enabled, too. It sets followup-to-honour and reply-to-honour to comply with reply address desires ๐ซถ.
Credentials and other settings are easily addressable by grouping them via account ๐. The file mode creation mask can be managed with umask ๐๏ธ. Files and shell pipe output can be sourced for evaluation, also during startup from within the "Resource files" ๐ง. Informational context can be available by setting verbose or debug (as via -v, -d) ๐.
๐ฌ On sending mail, and non-interactive mode
okay bestie, so if you want to send a message to one or more people, S-nail hands the actual delivery to a local or built-in mta (Mail-Transfer-Agent) transport. You just pass the names of the people as arguments, and use -b and -c to add (blind) carbon copy receivers ๐โจ
# Via test MTA
$ echo Hello, world | s-nail -:/ -Smta=test -s test $LOGNAME
# Via sendmail(1) MTA
$ </dev/null s-nail -:x -s test $LOGNAME
# Debug dry-run mode:
$ </dev/null LC_ALL=C s-nail -d -:/ \
-Sttycharset=utf8 -Sfullnames \
-b bcc@exam.ple -c cc@exam.ple -. \
'(Lovely) Bob <bob@exam.ple>' eric@exam.ple
# With SMTP (no real sending due to -d debug dry-run)
$ LC_ALL=C s-nail -d -:/ -Sv15-compat -Sttycharset=utf8 \
-S mta=smtps://mylogin@exam.ple:465 -Ssmtp-auth=none \
-S from=scriptreply@exam.ple \
-a /etc/mail.rc --end-options \
eric@exam.ple < /tmp/letter.txt
Email addresses and plain user names go through alternates filtering, and names on their own get expanded through alias and mta-aliases first ๐ฆ An address in angle brackets that is only a valid local user, like <name>, gets turned into a fully qualified address if hostname is unset or set to a non-empty value. If hostname is set to the empty value, the conversion is left up to the mta ๐ซถ Setting expandaddr gives you fine-grained control over recipient address types other than user names and network addresses, so you can be picky about it ๐
Recipients get sorted into types like this, and it is literally so specific ๐ญ
- Any name starting with a vertical bar
|is a command pipe: the command string after the|gets executed, and the message is sent to its standard input. - Any name that is only a hyphen-minus
-, or starts with a solidus/or the dot solidus./, is treated as a file, no matter what else is in there. - Any other name containing a commercial at
@is a network address. - Any other name starting with a plus sign
+is a mailbox name. - Any other name that contains a solidus
/, but has no exclamation mark!or percent sign%character before it, is also a mailbox name. - Whatever is left over is treated as a network address.
You can skip this whole classification thing by using a Fcc: header, see โCompose modeโ ๐ธ
$ echo bla | s-nail -Sexpandaddr -s test ./mbox.mbox
$ echo bla | s-nail -Sexpandaddr -s test '|cat >> ./mbox.mbox'
$ echo safe | LC_ALL=C \
s-nail -:/ -Smta=test -Sv15-compat -Sttycharset=utf8 \
--set mime-force-sendout --set fullnames \
-S expandaddr=fail,-all,+addr,failinvaddr -s test \
--end-options 'Imagine John <cold@turk.ey>'
Before a message gets sent, it goes through editing in โCompose modeโ โ๏ธ A lot of settings are static, though, and can be set more generally ๐ The envelope sender address is defined by from, and explicitly setting an originating hostname might be a vibe, especially with the built-in SMTP Mail-Transfer-Agent mta ๐ โCharacter setsโ for outgoing message and MIME part content are configurable via sendcharsets, while input data is assumed to be in ttycharset. Message data gets passed over the wire in a mime-encoding, and MIME parts (aka attachments) need a mimetype, which is usually taken out of โThe mime.types filesโ ๐ Saving copies of sent messages in a record mailbox might be desirable too. Just know that for most mailbox folder targets, โFilename transformationsโ will be performed ๐ง
For arranging a whole environment of settings that you can switch to with one single command or command line option, there are accounts ๐ Or you can go with a flat configuration, using so-called variable chains. These automatically pick โUSER@HOSTโ or โHOSTโ context-dependent variants that some variables support. For example, addressing โFolder pop3://yaa@exam.pleโ would find pop3-no-apop-yaa@exam.ple, pop3-no-apop-exam.ple and pop3-no-apop, in that order ๐ For more, please see โOn URL syntax and credential lookupโ and โINTERNAL VARIABLESโ โจ
To avoid environmental noise, scripts should create a script-local environment. Ideally, use the command line options -: to disable configuration files, together with repetitions of -S to specify variables ๐ซถ
$ env LC_ALL=C s-nail -:/ \
-Sv15-compat \
-Sttycharset=utf-8 -Smime-force-sendout \
-Sexpandaddr=fail,-all,failinvaddr \
-S mta=smtps://mylogin@exam.ple:465 -Ssmtp-auth=login \
-S from=scriptreply@exam.ple \
-s 'Subject to go' -a attachment_file \
-Sfullnames -. \
'Recipient 1 <rec1@exam.ple>' rec2@exam.ple \
< content_file
As shown, scripts that produce messages can โfakeโ a locale environment ๐ญ The example above sets the all-compatible 7-bit clean LC_ALL โCโ, but it will still take and send UTF-8 in the message text by using ttycharset ๐ If character set conversion is compiled in (features includes the term ,+iconv,), invalid character input data (according to ttycharset) would normally cause errors. Setting mime-force-sendout will instead, as a last resort, classify the input as binary data, so message creation can still succeed ๐ (Such content can then be inspected either by installing a pipe-TYPE/SUBTYPE handler for โapplication/octet-streamโ, or possibly automatically through mime-counter-evidence.)
In interactive mode, which we'll get into soon, you can send messages by calling the mail command with a list of recipient addresses ๐
$ s-nail -:/ -Squiet -Semptystart -Sfullnames -Smta=test
"/var/spool/mail/user": 0 messages
? mail "Recipient 1 <rec1@exam.ple>", rec2@exam.ple
...
? # Will do the right thing (tm)
? m rec1@exam.ple rec2@exam.ple
๐ Compose mode
If standard input is a terminal instead of the message you want to send, you're expected to type in the message contents yourself โจ๏ธ In compose mode, lines beginning with the character ~ (in fact the value of escape) are special. These are so-called โCOMMAND ESCAPESโ and they can read in files, process shell commands, add and edit attachments and more ๐ฅ For example, ~v or ~e will start the VISUAL text EDITOR, respectively, so you can revise the message in its current state. ~h lets you edit the most important message headers, and with the potent ~^ you can create custom headers, for example (more specifically than with -C and customhdr) ๐ Optionally, ~? gives an overview of most of the other available command escapes ๐
To create file-carbon-copies, the special recipient header Fcc: can be used as often as you like, for example via ~^ ๐ฆ Its entire value (or body, in standard terms) is interpreted as a folder target, after being subject to โFilename transformationsโ. This is the only way to create a file-carbon-copy without introducing an ambiguity about how the address is interpreted, so file names with leading vertical bars or commercial ats can be used here ๐
Like all other recipients, Fcc: is subject to the checks of expandaddr. Any local file and pipe command addressee honours the setting of mbox-fcc-and-pcc ๐ซถ
Once you're done editing, the command escape~.(see there) will call hooks, insert automatic injections and receivers, leave compose mode and send the message once it is completed ๐จ Want to abort instead? Use either~xor~q. The latter saves the message in the file denoted by DEAD, unless nosave is set ๐ญ And unless ignoreeof is set, the effect of~.can also be achieved by typing end-of-transmission (EOT) via โcontrol-Dโ (โ^Dโ) at the beginning of an empty line.~qis always reachable by typing end-of-text (ETX) twice via โcontrol-Cโ (โ^Cโ) ๐
The compose mode hooks on-compose-enter, on-compose-splice, on-compose-leave and on-compose-cleanup can be set to defined macros. They give you reliable and increasingly powerful ways to make automated message adjustments based on message context, like adding message signatures (message-inject-head, message-inject-tail) or creating additional receiver lists (also by setting autocc, autobcc) ๐ To do that, the command digmsg can be used to query and adjust the status of message(s) ๐ง The splice hook can also make use of โCOMMAND ESCAPESโ. ([v15 behaviour may differ] The compose mode hooks work for forward, mail, reply and variants. resend and Resend only provide the hooks on-resend-enter and on-resend-cleanup, which are pretty restricted due to the nature of the operation ๐)
๐ On reading mail, and more on interactive mode
When you invoke S-nail without addressees, it enters interactive mode, where you can read mails ๐ฌ In that case, the user's system inbox is read in (for more on mailbox types, please see the command folder). A one-line header of each message in there is displayed if the variable header is set โจ The visual style of this header summary can be adjusted with the variable headline, and the possible sorting criterion can be set via autosort ๐ You can scroll through screenfuls of headers with the command z ๐ฆ If the initially opened mailbox is empty, S-nail will instead exit immediately (after displaying a message), unless the variable emptystart is set ๐ฅบ
At the prompt, the command list gives a listing of all available commands, and help optionally gives a summary of some common ones ๐
If the optional documentation strings are available (see features), you can type help X (or ?X) to see the actual expansion of X and what its purpose is. Commands can be abbreviated, which is so iconic. Just note that POSIX defines some abbreviations, so the alphabetical order of commands does not necessarily match the abbreviations. It is however possible to define overwrites with commandalias ๐ธ These commands can also produce more verbose output ๐ฃ
Messages are numbered (starting at 1), and those numbers uniquely identify each message ๐ข The current message, the โdotโ, will be either the first new message, or the first unread message, or the first message of the mailbox ๐ฏ The internal variable showlast will instead make the last message the one used for this purpose ๐ The command headers displays a screenful of header summaries containing the โdotโ. The command from displays only the summaries of the given messages, defaulting to the โdotโ ๐
Okay bestie, so message content gets displayed with the command type (t, alias print) ๐ The variable crt decides whether and when S-nail uses your configured PAGER for display instead of writing straight to the terminal screen ๐ฅ๏ธ That's the only difference from more, which will always use the PAGER ๐
The command top only shows the first toplines lines of a message, and it might even squish them if topsqueeze is set ๐ค Your display experience can get a glow-up by setting and adjusting mime-counter-evidence, and you should also check out โHTML mail and MIME attachmentsโ โจ
By default the current message (โdotโ) gets displayed, but like with a lot of other commands you can give a fancy message specification (see โSpecifying messagesโ) ๐ง For example, t:u displays all unread messages, t. displays the โdotโ, t 1 5 types messages 1 and 5, t 1-5 types messages 1 through 5, and t- and t+ display the previous and the next message, respectively ๐ฆ The command search (a more substantial alias for from) displays a header summary of the given message specification list instead of their content. So if you want to search for subjects, it's giving:
? from '@Some subject to search for'
In the default setup all header fields of a message get typed, but you can white- or blacklist fields for a bunch of different situations using headerpick ๐ For example, to restrict the display to a super restricted set for type: headerpick type retain from to cc subject ๐ To display all header fields regardless of the currently active ignore or retain lists, use the commands Type and Top. Show will show the raw message content ๐ Note that historically the global s-nail.rc not only adjusts the list of displayed headers, but also sets crt. ([v15 behaviour may differ] A yet somewhat restricted) Reliable, scriptable message inspection is available via digmsg ๐ค
Dependent upon the configuration, a line editor (see the section โOn terminal control and line editorโ) aims at making the user experience with the many โCOMMANDSโ a bit nicer โญ When reading the system inbox, or when -f (or folder) specified a mailbox explicitly prefixed with the special %: modifier (to propagate it to a โprimary system mailboxโ), then messages which have been read (see โMessage statesโ) will be automatically moved to a โsecondary mailboxโ, the user's MBOX file, when the mailbox is left. That happens either by changing the active mailbox or by quitting S-nail. This automatic move from a system- or primary- to the secondary mailbox is not performed when the variable hold is set ๐ธ Messages can also be explicitly moved to other mailboxes, whereas copy keeps the original message. write can be used to write out data content of specific parts of messages ๐
After examining a message, the user can reply r to the sender and all recipients (which will also be placed in To: unless recipients-in-cc is set), or Reply R exclusively to the sender(s) ๐ซถ To comply with the receiver's desired reply address, the โquadoptionโs followup-to-honour and reply-to-honour should usually be set. The commands Lreply and Lfollowup know how to apply a special addressee message, see โMailing listsโ. Dependent on the presence and value of quote, the message being replied to will be included in a quoted form. Forwarding a message will allow editing the new message: the original message will be contained in the message body, adjusted according to headerpick. It is possible to resend or Resend messages. The former will add a series of Resent- headers, whereas the latter will not. Different to newly created messages, editing is not possible, and no copy will be saved even with record unless the additional variable record-resent is set ๐ง When sending, replying or forwarding messages, comments and full names will be stripped from recipient addresses unless the internal variable fullnames is set.
Of course messages can be deleted d, and they can spring into existence again via undelete, or when the S-nail session is ended via the exit or xit commands to perform a quick program termination ๐ To end a mail processing session regularly and perform a full program exit, one may issue the command quit. It will, among others, move read messages to the โsecondary mailboxโ MBOX as necessary, discard deleted messages in the current mailbox, and update the line editor history-file (optional, see features). By the way, whenever the main event loop is about to look out for the next input line, it will trigger the hook on-main-loop-tick ๐
โจ HTML mail and MIME attachments โจ
HTML-only messages are becoming more and more common, and many messages come bundled with a whole bouquet of MIME (Multipurpose Internet Mail Extensions) parts and attachments ๐ธ To get a notion of MIME types there is a built-in default set, onto which the content of โThe mime.types filesโ will be added (as configured and allowed by mimetypes-load-control). Types can also become registered and listed with the command mimetype. To improve interaction with the faulty MIME part declarations of real life, mime-counter-evidence will allow verification of the given assertion, and the possible provision of an alternative, better MIME type ๐ Note that plain text parts will always be preferred in multipart/alternative MIME messages unless mime-alternative-favour-rich is set.
Whereas a simple HTML-to-text filter for displaying HTML messages is optionally supported (indicated by ,+filter-html-tagsoup, in features), MIME types other than plain text cannot be handled directly ๐ฅบ To deal with specific non-text MIME types or file extensions, programs need to be registered. They either prepare (re-)integrable plain text versions of their input (a mode which is called copiousoutput), or display the content externally, for example in a graphical window. The latter type is only considered by and for the command mimeview.
To install a handler program for a MIME type, an according pipe-TYPE/SUBTYPE variable needs to be set. To define a handler for a file extension, pipe-EXTENSION can be used, and these handlers take precedence ๐ฅ Optionally, mail user agent configuration is supported (see โThe Mailcap filesโ), and it will be queried for display or quote handlers after the former ones. Type-markers registered via mimetype are the last possible source for information on how to handle a MIME type.
For example, to display HTML messages integrated via the text browsers lynx(1) or elinks(1), register a MathML MIME type and enable its plain text display. To open PDF attachments in an external PDF viewer, asynchronously and with some other magic attached, do this ๐ฆ
? if "$features" !% ,+filter-html-tagsoup,
? #set pipe-text/html='?* elinks -force-html -dump 1'
? set pipe-text/html='?* lynx -stdin -dump -force_html'
? # Display HTML as plain text instead
? #set pipe-text/html=?t
? endif
? mimetype ?t application/mathml+xml mathml
? wysh set pipe-application/pdf='?&=? \
trap "rm -f \"${MAILX_FILENAME_TEMPORARY}\"" EXIT;\
trap "trap \"\" INT QUIT TERM; exit 1" INT QUIT TERM;\
mupdf "${MAILX_FILENAME_TEMPORARY}"'
? define showhtml {
? \localopts yes
? \set mime-alternative-favour-rich pipe-text/html=?h?
? \type "$@"
? }
? \commandalias html \\call showhtml
โจ Mailing lists โจ
Known or subscribed-to mailing lists may be flagged in the summary of headers (headline format character %L), and they gain special treatment when sending mails ๐ฌ The variable followup-to-honour will ensure that a Mail-Followup-To: header is honoured when a message is being replied to (reply, followup, Lreply, Lfollowup). followup-to controls creation of this header when creating mails, if the necessary user setup (from, sender) is available. Then, it may also be created automatically, for example when list-replying via Lreply or Lfollowup, or when followup or reply is used and the message's Mail-Followup-To: is honoured, etc. ๐
The commands mlist and mlsubscribe manage S-nail's notion of which addresses are mailing lists ๐ With the optional regular expression support, any address which contains any of the magic regular expression characters (^[*+?|$; see re_format(7) or regex(7), dependent on the host system) will be compiled and used as one, possibly matching many addresses. It is not possible to escape the โmagicโ. In order to match special characters as-is, bracket expressions must be used, for example search @subject@'[[]open bracket'.
? set followup-to followup-to-honour=ask-yes \
reply-to-honour=ask-yes
? mlist a1@b1.c1 a2@b2.c2 '.*@lists\.c3$'
? mlsubscribe a4@b4.c4 exact@lists.c3
Known and subscribed lists differ in that, for the latter, the user's address is not part of a generated Mail-Followup-To:. There are exceptions, for example if multiple lists are addressed and not all of them have the subscription attribute ๐ค When replying to a message, its list address (List-Post: header) is automatically and temporarily treated like a known mlist. Dependent on the variable reply-to-honour, an existing Reply-To: is used instead (if it is a single address on the same domain as List-Post:), in order to accept a list administrator's wish that is supposed to have been manifested like that ๐ง
For convenience and compatibility with mail programs that do not honour the non-standard M-F-T, an automatic user entry in the carbon-copy Cc: address list of generated messages can be created by setting followup-to-add-cc. This entry will be added whenever the user will be placed in the Mail-Followup-To: list and is not a regular addressee already. reply-to-swap-in tries to deal with the address rewriting that many mailing lists nowadays perform to work around DKIM / DMARC etc. standard-imposed problems ๐
โจ Signed and encrypted messages with S/MIME โจ
Optionally, S/MIME provides two central mechanisms: message signing and message encryption ๐ A signed message contains some data in addition to the regular text. That data can be used to verify that the message has been sent using a valid certificate, that the sender address matches the one in the certificate, and that the message text has not been altered ๐ต๏ธ Signing a message does not change its regular text, so it can be read regardless of whether the recipient's software is able to handle S/MIME. It is thus usually possible to sign all outgoing messages if so desired โจ
Okay bestie, encryption is basically invisible ink ๐ซฃ๐. It hides the message text from literally everyone except whoever holds the secret decryption key ๐๏ธ. To encrypt something for someone, you need that specific recipient's public encryption key first ๐. So you cannot send encrypted mail to people unless you already have their key, either from earlier chats or from public key directories ๐. And since signing uses private keys while encryption uses public keys, you should always sign before you encrypt ๐ โจ.
A central concept in S/MIME is the certification authority, or CA ๐. A CA is a trusted institution that issues certificates. Every certificate can be checked to really come from its CA, as long as you already know the CA's own certificate ๐ต๏ธโโ๏ธ. A set of CA certificates usually comes bundled and installed with the cryptographic library on your local system ๐ฆ, so reasonable S/MIME security on the Internet depends on whether the source of that library install is trustworthy ๐ซถ. You can also use your own pool of trusted certificates ๐ธ. If you want that, set smime-ca-no-defaults to skip the default pool, then point smime-ca-file and/or smime-ca-dir at your trusted pool. Just remember that a certificate is never more secure than the method its CA certificate was retrieved with ๐.
This trusted pool is what the verify command uses to make sure an S/MIME message can actually be trusted ๐. If it checks out, the verified sender certificates embedded in signed messages can be saved locally with certsave. After that, S-nail uses them to encrypt future messages to those senders ๐ฆ:
? certsave FILENAME
? set smime-encrypt-USER@HOST=FILENAME \
smime-cipher-USER@HOST=AES256
To sign outgoing messages, so receivers can verify where they came from โ๏ธ, you need a personal S/MIME certificate. S-nail supports password-protected personal certificates and keys too ๐. Check out smime-sign-cert for the details. The section "On URL syntax and credential lookup" covers every place user credentials can come from, and "S/MIME step by step" walks through an example of how to get your own private S/MIME certificate ๐ง. Generally, if you already have a private key plus certificate "pair", all you need to do is set a few variables:
? set smime-sign-cert=ME@exam.ple.paired \
smime-sign-digest=SHA512 \
smime-sign from=myname@my.host
Here are the variables that matter for S/MIME โจ:
- General S/MIME:
smime-ca-dir,smime-ca-file,smime-ca-flags,smime-ca-no-defaults,smime-crl-dirandsmime-crl-file. - Signing:
smime-sign,smime-sign-cert,smime-sign-include-certsandsmime-sign-digest. - En- and decryption:
smime-cipherandsmime-encrypt-USER@HOST. - Secondary:
content-description-smime-messageandcontent-description-smime-signature.
S/MIME is only available if ,+smime, is included in features ๐.
[v15 behaviour may differ] Heads up ๐ญ: neither S/MIME signing nor encryption applies to message subjects or other header fields yet. So those may not hold sensitive info for encrypted messages, and they can't be trusted even when the message content has been verified. When you send signed messages, repeat any important header info in the message text ๐.
๐ On URL syntax and credential lookup ๐
URLs (RFC 3986) are everywhere when it comes to protocol-specific resources ๐. Here they show up in a "normalized" flavour. They're meant as a compact, easy-to-read notation, not for data exchange, so they don't follow any real standard ๐. Optional bits go in brackets [], either because there's another way to define the info or because the part is protocol specific. /path, for example, is used by the [Option]al Maildir folder type and the IMAP protocol, but not POP3 ๐. If USER and PASSWORD go in a URL server specification, they need URL percent encoded (RFC 3986) forms, which you can make with urlcodec ๐:
PROTOCOL://[USER[:PASSWORD]@]server[:port][/path]
Lots of INTERNAL VARIABLES come in multiple versions, which this document calls "variable chains" ๐งฉ. There's the plain variable, plus variable-HOST and variable-USER@HOST. If a port was specified, HOST really means server:port, not server. And USER is never in URL percent encoded form ๐
. Say the hypothetical smtp://wings%3Aof@a.dove was used, with user and password included, or it was smtp://a.dove from some other source. To look up the chain tls-config-pairs, first tls-config-pairs-wings:of@a.dove is checked, then tls-config-pairs-a.dove, and only then the plain variable ๐ช.
The logic for collecting an account's credential information goes like this ๐ต๏ธ:
- User: A user is always required ๐ซถ. If no
USERcame in the URL, the variablesuser-HOSTanduserget looked up. After that, when enforced by the [Option]al variablesnetrc-lookup-HOSTornetrc-lookup, the user's "The .netrc file" gets searched for aHOSTspecific entry that provides aloginname. Only unambiguous entries are used, meaning exactly one possible matching entry forHOST๐. If there's still noUSER, the verifiedLOGNAMEis used, since it's known to be a valid user on the current host ๐. - Authentication: Unless otherwise noted, the chain
PROTOCOL-auth-USER@HOST,PROTOCOL-auth-HOST,PROTOCOL-authgets checked, falling back to a protocol-specific default when necessary ๐. - Password: If no
PASSWORDwas given in the URL, then if theUSERwas found through the [Option]alnetrc-lookup, that lookup may have provided the password too ๐๏ธ. Otherwise, the chainpassword-USER@HOST,password-HOST,passwordgets looked up. Once the (now complete) [Option]al chainnetrc-lookup-USER@HOST,netrc-lookup-HOST,netrc-lookupis checked, and if it's set, the netrc cache is searched for a password only. Multiple user accounts for a single machine can exist, and so can a fallback entry with no user but with a password ๐ง . If there's still no password but the protocol's chosen authentication type needs one, then in interactive mode you'll get prompted on the terminal โจ๏ธ.
Note: S/MIME verification works relative to the values in the From: (or Sender:) header field(s) ๐. That means the values of smime-sign, smime-sign-cert, smime-sign-include-certs and smime-sign-digest aren't looked up using the USER and HOST chains above. Instead, they use the corresponding values from the message being worked on. If no address matches, we assume and use the setting of from. In unusual cases, multiple and different USER and HOST combinations may be involved. On the other hand, those unusual cases become possible ๐. The usual case is as short as:
set mta=smtp://USER:PASS@HOST smtp-use-starttls \
smime-sign smime-sign-cert=+smime.pair \
from=myname@my.host
The section "EXAMPLES" has complete example configurations ๐.
๐ Encrypted network communication ๐
[Option] SSL (Secure Sockets Layer), and its successor TLS (Transport Layer Security), are protocols that help secure communication by setting up a safely initiated, encrypted network connection ๐ซง. Certificates are the central TLS concept. As part of each connection setup, a (set of) certificate(s) gets exchanged, which lets the identity of the network peer be cryptographically verified. If possible, the TLS/SNI (ServerNameIndication) extension is enabled too, so servers get fine-grained control over which certificates are used ๐ฏ. A locally installed pool of trusted certificates is then inspected, and verification succeeds if it contains a(n in)direct signer of the presented certificate(s) โ .
The local pool of trusted so-called CA (Certification Authority) certificates is usually delivered with, and used alongside, the TLS library ๐. To pick a custom pool, point tls-ca-file and/or (with special preparation) tls-ca-dir at the location you want. Setting tls-ca-no-defaults in addition avoids inspecting the default pool too ๐
โโ๏ธ. And again, a certificate can't be more secure than the method its CA certificate was retrieved with ๐ซ . For inspection or other purposes, you can fetch a server's certificate (as seen when connecting to it) with the tls command. The port can usually be the protocol name too, and tls-verify is taken into account here ๐:
$ s-nail -vX 'tls certchain SERVER-URL[:PORT]; x'
A local CA pool isn't strictly necessary, though ๐, because server certificates can also be verified via their fingerprint. For this, a message digest gets calculated and compared against the variable chain tls-fingerprint, and verification succeeds if the fingerprint matches ๐ซ. The digest algorithm is configured via the variable chain tls-fingerprint-digest, and again, tls can be used:
$ s-nail -X 'wysh set verbose; tls fingerprint SERVER-URL[:PORT]; x'
Whether encrypted communication is possible, and which configuration steps you need to enable it, depends on the protocol ๐งฉ. Some protocols, like POP3S, are implicitly encrypted. Others, like POP3, can upgrade a plain text connection if requested. For example, to use the STLS that POP3 offers, (a member of) the variable (chain) pop3-use-starttls needs to be set. With convenience via shortcut ๐:
shortcut encpop1 pop3s://pop1.exam.ple
shortcut encpop2 pop3://pop2.exam.ple
set pop3-use-starttls-pop2.exam.ple
set mta=smtps://smtp.exam.ple:465
set mta=smtp://smtp.exam.ple smtp-use-starttls
Okay so normally that's all you have to do, bestie, because TLS libraries try to give you safe defaults ๐โจ. But there are plenty of knobs if you wanna tweak stuff ๐
. Certificate verification can be fine-tuned via tls-ca-flags, and the TLS config basics live in tls-config-pairs, which is where you control things like protocol versions or cipher lists ๐. Earlier on, we hinted at how to lock the protocols down to highly secure ones, but as of writing, the protocol and cipher lists may need to be relaxed so you can connect to some servers ๐ญ. For example, the following lets you connect to a โLionโ running OpenSSL 0.9.8za from June 2014 ๐ฆ (check โINTERNAL VARIABLESโ for more on variable chains):
wysh set tls-config-pairs-lion@exam.ple='MinProtocol=TLSv1.1,\
CipherString=TLSv1.2:!aNULL:!eNULL:\
ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:\
DHE-RSA-AES256-SHA:@STRENGTH'
When you're building your own cipher list, the OpenSSL program ciphers(1) is the one to look at ๐ง. The TLS-related variables worth knowing are tls-ca-dir, tls-ca-file, tls-ca-flags, tls-ca-no-defaults, tls-config-file, tls-config-module, tls-config-pairs, tls-crl-dir, tls-crl-file, tls-rand-file and tls-verify. Also see tls-features. TLS is only available if +tls is included in features ๐ธ.
๐ Character sets ๐
[Option] The user's locale is detected by looking at the LC_ALL environment variable ๐. The internal variable ttycharset gets set to the detected terminal character set, so it'll show up in the output of commands like set and varshow โจ. That character set is the one targeted when displaying data, and user input is expected to be in it too ๐.
When you create messages, their character input data gets classified ๐๏ธ. 7-bit clean text and attachments get classified as charset-7bit. 8-bit data will [Option]ally be converted into members of sendcharsets until one of the conversions succeeds, and charset-8bit is the implied default last member of that list ๐ซถ. If no 8-bit character set can represent your input data, the message won't be sent, and its text can optionally be saved in DEAD ๐. If that's not acceptable (say, in script environments), mime-force-sendout can be set to force non-convertible data out as application/octet-stream classified binary content instead ๐ฅ. That way receivers still get to inspect the message content, for example via mime-counter-evidence ๐ต๏ธโโ๏ธ.
If the [Option]al character set conversion isn't available (features misses ,+iconv,), then ttycharset is the only supported character set for non 7-bit clean data, and it's simply assumed it can be used to exchange 8-bit messages ๐.
ttycharset can also be given an explicit value so you can send mail in a completely โfakedโ locale environment ๐ญ. This is useful for generating and sending stuff like 8-bit UTF-8 input data from a pure 7-bit US-ASCII LC_ALL=C environment (there's an example of this in the section โOn sending mail, and non-interactive modeโ). Heads up though: reading mail won't really work as expected in a faked environment โ ๏ธ. Even though ttycharset might be addressable, any output gets made safely printable (as via vexpr makeprint) according to the actual locale environment, which ttycharset doesn't affect ๐.
Classifying 7-bit clean data as charset-7bit is a problem if the input character set (ttycharset) is a multibyte character set that's itself 7-bit clean ๐ฌ. The Japanese character set ISO-2022-JP is one of these. It's 7-bit clean, but it can encode the rich set of Japanese Kanji, Hiragana and Katakana characters ๐ฏ๐ต. To tell receivers about this character set, the mail message has to be MIME encoded so ISO-2022-JP can be advertised, otherwise you'd end up with an invalid email message, and nobody wants that ๐. To do this, set the variable charset-7bit to ISO-2022-JP. (Honestly, a better approach for email these days is UTF-8, which uses 8-bit bytes for non-US-ASCII data ๐.)
When you're replying to a message and reply-in-same-charset is set, the character set of the message being replied to is tried first as a target character set ๐ฏ. (It's still subject to charsetalias filtering though.) Another option is sendcharsets-else-ttycharset, which reflects the user's locale environment automatically. It treats ttycharset as an implied member of an unset sendcharsets ๐.
[Option] When reading messages, their text data gets converted into ttycharset as necessary so it can be displayed on your terminal ๐. Unprintable characters and invalid byte sequences are detected and replaced by substitution characters ๐. You can set up character set mappings for source character sets with charsetalias, which is handy for working around faulty or incomplete character set catalogues (you could, for example, add a missing LATIN1 to ISO-8859-1 mapping) ๐ ๏ธ. You can also use it to enforce treating one character set as another one (like โinterpret LATIN1 as CP1252โ) ๐งฉ. And see charset-unknown-8bit for dealing with another hairy aspect of message interpretation ๐ตโ๐ซ.
If you ever see a message saying โcannot convert from a to bโ, it means either some characters aren't appropriate for the currently selected (terminal) character set, or the system doesn't support the conversion you need ๐จ. In the first case, you'll need to set an appropriate LC_CTYPE locale and/or the variable ttycharset. The best results are usually achieved in a UTF-8 locale on a UTF-8 capable terminal, where the full Unicode spectrum of characters is available ๐ธ. In that setup you can display characters from all over the world, while still using simpler character sets for sending when you want maximum compatibility with older mail clients ๐ฆ.
On the other hand, the POSIX standard defines a locale-independent 7-bit โportable character setโ that you should use when overall portability is an issue ๐ง. Even more restricted is the subset named โportable filename character setโ, which consists of A-Z, a-z, 0-9, period ., underscore _ and hyphen-minus - ๐
.
๐ Message states ๐
S-nail tells messages apart by their state ๐ซง. The current state is reflected in the summary of headers if the attrlist of the configured headline allows it, and โSpecifying messagesโ based on state is possible too โจ. When you operate on the system inbox, or any other โprimary system mailboxโ, special actions can kick in when you leave the mailbox (this includes implicit leaving via program termination, unless the exit command was used). Things like automatically moving messages to the โsecondary mailboxโ MBOX ๐ฆ. This can be irritating if you're used to โmore modernโ mail-user-agents, so the provided global s-nail.rc template sets the internal hold and keepsave variables to suppress this behaviour ๐.
newโ The message hasn't been viewed or moved to any other state yet ๐ฃ. These messages are kept even in the โprimary system mailboxโ.unreadโ The message hasn't been viewed or moved to any other state, but it was already there the last time the mailbox got opened ๐. These messages are kept even in the โprimary system mailboxโ.readโ The message was processed by one of these commands:~f,~m,~F,~M,copy,mbox,next,pipe,Print,print,top,Type,type,undeleteโ . The commandsdpanddtwill always try to automatically โstepโ and type the โnextโ logical message, so they can mark multiple messages as read ๐โโ๏ธ. Thedeletecommand will do so if the internal variableautoprintis set. Except when theexitcommand is used, messages in a โprimary system mailboxโ that are inreadstate when you leave the mailbox get saved in the โsecondary mailboxโ MBOX, unless the internal variableholdis set ๐ฅ.deletedโ The message was processed by one of these commands:delete,dp,dt๐๏ธ. Onlyundeletecan be used to get to these messages again ๐.preservedโ The message was processed by apreservecommand and it'll be kept in its current location ๐.savedโ The message was processed bysaveorwrite๐พ. Unless theexitcommand is used, messages in a โprimary system mailboxโ that are insavedstate when you leave the mailbox will be deleted ๐. They'll be saved in the โsecondary mailboxโ MBOX instead when the internal variablekeepsaveis set ๐ซถ.
On top of these message states, there are flags. These don't have any technical meaning in the mail system, except that they let you address messages in special ways when โSpecifying messagesโ ๐. Flags are saved with messages, so they're persistent, and they're portable between a set of widely used MUAs ๐.
answeredโ Marks messages as having been answered ๐ฌ.draftโ Marks messages as being a draft โ๏ธ.flagโ Marks messages which need special attention ๐ฉ.
๐ฏ Specifying messages ๐ฏ
[Only new quoting rules] COMMANDS that take โMessage list argumentsโ, like search, type, copy and delete, can perform actions on a bunch of messages at once ๐ซง. If you specify invalid messages or use illegal syntax, errors get reported through the โINTERNAL VARIABLESโ !, ^ERR and companions, as well as the command exit status ? ๐จ.
For example, delete 1 2 deletes messages 1 and 2, whereas delete 1-5 deletes messages 1 through 5 ๐. In sorted or threaded mode (see the sort command), delete 1-5 deletes the messages located between (and including) messages 1 through 5 in the sorted/threaded order, as shown in the headers summary ๐งต.
Errors can pop up for a few reasons ๐ญ. ^ERR-BADMSG shows up when you request an invalid message, and ^ERR-NOMSG if no applicable message can be found. ^ERR-CANCELED is for missing informational data (mostly thread-related). ^ERR-INVAL covers invalid syntax, and ^ERR-IO covers input/output errors. The following special message names exist ๐:
.โ The current message, aka the so-called โdotโ ๐ต.;โ The message that was previously the current message ๐ฐ๏ธ. It needs to be quoted.,โ The parent message of the current message. That's the message with the Message-ID given in theIn-Reply-To:field, or the last entry of theReferences:field of the current message ๐งโโ๏ธ.-โ ๐ the previous undeleted message! For theundeletecommand, it's the previous deleted message instead. In sorted orthreaded mode, it's the previous such message in that order ๐งต+โ โญ๏ธ the next undeleted message (or the next deleted message forundelete). In sorted orthreaded mode, it's the next such message in that order ๐^โ ๐ฅ the first undeleted message (or the first deleted message forundelete). In sorted orthreaded mode, it's the first such message in that order ๐ธ$โ ๐ the last message. In sorted orthreaded mode, it's the last such message in that order. Heads up: this one needs to be quoted, bestie! ๐&xโ ๐งต๐ inthreaded sort mode, this selects the message you address withx(wherexis any other message specification), plus all messages from the thread that begins at it. Otherwise, it's identical tox. If you leavexout, you get the thread that begins with the current message.*โ ๐ all messages, every single one!`โ ๐ซถ all messages that were included in the "Message list arguments" of the previous command. This one needs to be quoted! Okay, so a convenient way to read all new messages is to select them viafrom :n(more on that below), then read them in order with the default command, next, by just typing`over and over. For this to work,showlastmust be set. โจx-yโ ๐ข an inclusive range of message numbers. The endpoints can also be any of.;-+^$.addressโ ๐ฎ a case-insensitive "any substring matches" search against theFrom:header. It'll match addresses too, even if showname is set (POSIX says "any address as shown in a header summary shall be matchable in this form"). BUT if the allnet variable is set, only the local part of the address gets evaluated, the comparison is not case-insensitive, and showname is completely ignored ๐ โโ๏ธ. For finer control and match boundaries, use the@search expression./stringโ ๐ all messages that containstringin the subject field (case ignored according to locale). See also the searchheaders variable. Ifstringis empty, the string from the previous specification of that type gets reused, so no stress![@name-list]@exprโ ๐ต๏ธ all messages that contain the given case-insensitive search expression. If [Option]al regular expression support is available,exprwill be interpreted as an (extended) regex if any of the "magic regular expression characters" shows up. If the optional@name-listpart is missing, the search is restricted to the subject field body. Otherwise,name-listis a comma-separated list of header fields to search, for example:
'@to,from,cc@Someone i ought to know'
In order to search for a string that includes a @ (commercial at) character, the name-list is effectively non-optional, but it can be given as the empty string. Also, specifying an empty search expression will effectively test for the existence of the given header fields. Some special header fields can be abbreviated: f, t, c, b and s will match From, To, Cc, Bcc and Subject, respectively and case-insensitively. [Option]ally, and just like expr, name-list will be interpreted as an (extended) regular expression if any of the "magic regular expression characters" is seen.
The special names header or < can be used to search in (all of) the header(s) of the message. The special names body or > and text or = will perform full text searches. The former searches only the body, while the latter also searches the message header ([v15 behaviour may differ] this mode yet brute force searches over the entire decoded content of messages, including administrativa strings). ๐ณ
This specification performs a full text comparison, but even with regular expression support it's almost impossible to write a search expression that safely matches only a specific address domain. To request that the body content of the header is treated as a list of addresses, and to strip those down to the plain email address which the search expression is to be matched against, prefix the effective name-list with a tilde ~:
'@~f,c@@a\.safe\.domain\.match$'
:cโ ๐ all messages of a state, or with a matching conditionc, wherecis one or more of the following colon modifiers:aโ answered messages (cf. the variable markanswered) ๐จ๏ธdโ "deleted" messages (for the undelete and from commands only) ๐๏ธfโ flagged messages ๐ฉLโ messages with receivers that match mlsubscribed addresses ๐ฌlโ messages with receivers that match mlisted addresses ๐nโ "new" messages ๐oโ old messages (any not in state "read" or "new") ๐งrโ "read" messages ๐Sโ [Option] messages with unsure spam classification (see "Handling spam") ๐คsโ [Option] messages classified as spam ๐ซtโ messages marked as draft โ๏ธuโ "unread" messages ๐
[Option] IMAP-style SEARCH expressions may also be used! These consist of keywords and criterions. Because "Message list arguments" are split into tokens according to "Shell-style argument quoting," it's necessary to quote the entire IMAP search expression so it stays a single token. This addressing mode is available with all types of mailbox folders, and S-nail will perform the search locally as necessary. โจ Strings must be enclosed by double quotation marks " in their entirety if they contain whitespace or parentheses. Within the quotes, only reverse solidus \ is recognized as an escape character. All string searches are case-insensitive. When the description says the "envelope" representation of an address field is used, that means the search string is checked against both a list constructed as
'("name" "source" "local-part" "domain-part")'
for each address, and the addresses without real names from the respective header field. These search expressions can be nested using parentheses. See below for examples!
(criterion)โ all messages that satisfy the given criterion.(criterion1 criterion2 ... criterionN)โ all messages that satisfy all of the given criteria.(or criterion1 criterion2)โ all messages that satisfy either criterion1 or criterion2, or both. To connect more than two criteria usingorspecifications, they have to be nested using additional parentheses, as with(or a (or b c)), since(or a b c)really means((a or b) and c). For a simpleoroperation of independent criteria on the lowest nesting level, you can achieve similar effects by using three separate criteria, as with(a) (b) (c).(not criterion)โ all messages that do not satisfy criterion.(bcc "string")โ all messages that contain string in the envelope representation of theBcc:field.(cc "string")โ all messages that contain string in the envelope representation of theCc:field.(from "string")โ all messages that contain string in the envelope representation of theFrom:field.(subject "string")โ all messages that contain string in theSubject:field.(to "string")โ all messages that contain string in the envelope representation of theTo:field.(header name "string")โ all messages that contain string in the specifiedName:field.(body "string")โ all messages that contain string in their body.(text "string")โ all messages that contain string in their header or body.(larger size)โ all messages that are larger than size (in bytes).(smaller size)โ all messages that are smaller than size (in bytes).(before date)โ all messages that were received before date, which must be in the formd[d]-mon-yyyy. Heredis the day of the month as one or two digits,monis the name of the month (one ofJan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec), andyyyyis the year as four digits, for example28-Dec-2012. ๐(on date)โ all messages that were received on the specified date.(since date)โ all messages that were received since the specified date.(sentbefore date)โ all messages that were sent on the specified date.(senton date)โ all messages that were sent on the specified date.(sentsince date)โ all messages that were sent since the specified date.()โ the same criterion as for the previous search. This specification cannot be used as part of another criterion. If the previous command line contained more than one independent criterion, the last of those criteria is used. ๐
โจ On terminal control and line editor โจ
[Option] Terminal control through one of the standard Unix libraries may be available: the Termcap Access Library (libtermcap, -ltermcap) or the Terminal Information Library (libterminfo, -lterminfo). ๐ฅ๏ธ For the TERMinal defined in the environment, interactive usage aspects, for example "Coloured display," and insight of cursor and function keys for the Mailx-Line-Editor (MLE), will be enhanced or enabled. Library interaction can be disabled on a per-invocation basis via termcap-disable, whereas the internal variable termcap is always used as a preferred source of terminal capabilities. ๐ (For a usage example, see the โFAQโ entry โNot "defunctional", but the editor key does not workโ.)
Okay so the built-in Mailx-Line-Editor (MLE) is [Option] and should work in every environment that follows the ISO C standard ISO/IEC 9899/AMD1:1995 (โISO C90, Amendment 1โ) ๐
. It'll even support wide glyphs if possible! That functionality was dropped from ISO C, but it's still in the X/Open Portability Guide Issue 4 (โXPG4โ) โจ. If you ever want to stop anyone from using a line editor in interactive mode, set line-editor-disable ๐. Especially if the [Option]al terminal control support is missing, setting entries in termcap will help if the MLE starts misbehaving, so check there for more ๐ ๏ธ. The MLE can also support a little bit of colour ๐.
[Option] If the history feature is available, input from line editor prompts gets saved in a history list ๐ that you can search in and expand from ๐. Want something not saved? Prefix it with any amount of whitespace and it's gone from history ๐. Aspects of history, like allowed content, maximum size, and whether history gets saved persistently, are all configurable with the internal variables history-file, history-gabby, history-gabby-persist and history-size ๐. There's also the macro hook on-history-addition, which gives you finer control over what actually enters history ๐ง.
The MLE supports a whole set of editing and control commands ๐. By default, as many of these as possible get assigned to single-letter control codes, which should work on any terminal. You make one by holding the โcontrolโ key while pressing the key you want, for example โcontrol-Dโ โจ๏ธ. If the [Option]al bind command is available, the MLE commands can also be accessed freely. You assign the command name (shown in parentheses in the list below) to any key-sequence you like, and the MLE will instead, and also, use bind to set up its built-in key bindings (more of them if the [Option]al terminal control is available). That whole default setup can be suppressed completely by setting line-editor-no-defaults ๐ซถ. โShell-style argument quotingโ notation is used in the list below ๐:
\cAโ Go to the start of the line (mle-go-home) ๐ .\cBโ Move the cursor backward one character (mle-go-bwd) โฌ ๏ธ.\cCโraise(3)SIGINT(mle-raise-int) ๐ฅ.\cDโ Forward delete the character under the cursor. It quits S-nail if used on the empty line, unless the internal variableignoreeofis set (mle-del-fwd) ๐ญ.\cEโ Go to the end of the line (mle-go-end) ๐.\cFโ Move the cursor forward one character (mle-go-fwd) โก๏ธ.\cGโ Cancel the current operation with a full reset ๐งน. If there's an active history search or tabulator expansion, this command resets that first and reverts to the former line content, so you'll need a second reset for a full reset in that case (mle-reset).\cHโ Backspace: backward delete one character (mle-del-bwd) ๐.\cIโ [Only new quoting rules] Horizontal tabulator. It tries to expand the word before the cursor, supporting the usual โFilename transformationsโ โจ. This one is affected bymle-quote-rndtripandline-editor-cpl-word-breaks(mle-complete).\cJโ Newline: commit the current line (mle-commit) โ .\cKโ Cut all characters from the cursor to the end of the line (mle-snarf-end) โ๏ธ.\cLโ Repaint the line (mle-repaint) ๐จ.\cNโ [Option] Go to the next history entry (mle-hist-fwd) ๐.\cOโ ([Option]ally context-dependent) Invokes the commanddt๐คทโโ๏ธ.\cPโ [Option] Go to the previous history entry (mle-hist-bwd) ๐.\cQโ Toggle roundtrip mode shell quotes, where produced, on and off (mle-quote-rndtrip). This setting is temporary and gets forgotten once the command line is committed, so also seeshcodec๐.\cRโ [Option] Complete the current line from the remaining older history entries (mle-hist-srch-bwd) ๐.\cSโ [Option] Complete the current line from the remaining newer history entries (mle-hist-srch-fwd) ๐.\cTโ Paste the snarf buffer (mle-paste) ๐.\cUโ The same as\cAfollowed by\cK(mle-snarf-line) ๐ซ.\cVโ Prompts for a Unicode character to be inserted. You type it as a hexadecimal number without prefix, seevexpr(mle-prompt-char) ๐ฆ. Heads up: this command needs to be assigned to a single-letter control code in order to be recognized and executed during input of a key-sequence. Only three single-letter control codes can be used for that shortcut purpose. This control code is then special-treated and thus cannot be part of any other sequence, because it triggersmle-prompt-charimmediately ๐ฑ.\cWโ Cut the characters from the one preceding the cursor back to the preceding word boundary (mle-snarf-word-bwd) ๐๏ธ.\cXโ Move the cursor forward one word boundary (mle-go-word-fwd) โฉ.\cYโ Move the cursor backward one word boundary (mle-go-word-bwd) โช.\cZโraise(3)SIGTSTP(mle-raise-tstp) ๐ด.\c[โ Escape: resets a possibly used multibyte character input state machine and [Option]ally a lingering, incomplete key binding (mle-cancel) ๐ช. This one also needs to be assigned to a single-letter control code to be recognized and executed during input of a key-sequence, since only three single-letter control codes can be used for that shortcut purpose. This control code may also be part of a multi-byte sequence. But if a sequence is active and this very control code is currently also an expected input, the active sequence takes precedence and consumes the control code ๐ค.\c\โ ([Option]ally context-dependent) Invokes the commandz+โ.\c]โ ([Option]ally context-dependent) Invokes the commandz$๐ฒ.\c^โ ([Option]ally context-dependent) Invokes the commandz00๏ธโฃ.\c_โ Cut the characters from the one after the cursor to the succeeding word boundary (mle-snarf-word-fwd) โ๏ธ.\c?โ Backspace:mle-del-bwd๐.
A few more commands don't get a control code in the list above, but they're still in the mix ๐:
mle-bellโ Ring the audible bell ๐.mle-clear-screen[Option] โ Move the cursor home and clear the screen ๐งน.mle-fullresetโ Different tomle-reset, this one immediately resets a possibly active search etc. ๐ฅ.mle-go-screen-bwdโ Move the cursor backward one screen width โฌ ๏ธ.mle-go-screen-fwdโ Move the cursor forward one screen width โก๏ธ.mle-raise-quitโraise(3)SIGQUIT๐จ.
Coloured display ๐
[Option] Colours and font attributes through ANSI a.k.a. ISO 6429 SGR (select graphic rendition) escape sequences are optionally supported ๐จ. Whether they actually get used depends solely on the capability of the detected terminal type (TERM), as fine-tuned through termcap ๐
. You manage colours and font attributes with the multiplexer command colour, and uncolour removes the given mappings. Setting colour-disable suppresses colour and font attribute sequences, but leaves established mappings unchanged ๐.
Whether colour and font attribute sequences should also be generated when output gets paged through the external PAGER (also see crt) depends on the setting of colour-pager. That's because pagers usually need to be configured before they support ISO escape sequences ๐. Knowledge of some widely used pagers is built in, though, and in a clean environment it's often enough to just set colour-pager. Check that variable for more on this topic ๐ซถ.
It might make sense to conditionalize your colour setup on interactive mode via if (โterminalโ indeed means โinteractiveโ) ๐ง :
if terminal && "$features" =% ,+colour,
colour iso view-msginfo ft=bold,fg=green
colour iso view-header ft=bold,fg=red (from|subject) # regex
colour iso view-header fg=red
uncolour iso view-header from,subject
colour iso view-header ft=bold,fg=magenta,bg=cyan
colour 256 view-header ft=bold,fg=208,bg=230 "subject,from"
colour mono view-header ft=bold
colour mono view-header ft=bold,ft=reverse subject,from
endif
Handling spam ๐ก๏ธ
[Option] S-nail can make use of several spam interfaces to identify spam and, in general, deal with spam messages ๐ต๏ธโโ๏ธ. A precondition for most commands to function is that the spam-interface variable is set to one of the supported interfaces ๐
. โSpecifying messagesโ that have been identified as spam is possible via their (volatile) is-spam state, using the :s and :S specifications. Their attrlist entries get used when displaying the headline in the summary of headers ๐.
spamraterates the given messages and sets theiris-spamflag accordingly ๐. If the spam interface offers spam scores, these can be shown in the headline by using the format%$.spamham,spamspamandspamforgetinteract with the Bayesian filter of the chosen interface and learn the given messages as โhamโ or โspamโ, respectively ๐ง . The last command can be used to cause โunlearningโ of messages. It adheres to their currentis-spamstate and thus reverts previous teachings ๐.spamclearandspamsetwill simply set and clear, respectively, the mentioned volatileis-spammessage flag, without any interface interaction ๐ช.
The spamassassin(1) based spam-interface spamc requires a running instance of the spamd(1) server in order to function ๐ฅ๏ธ. You start it with the option --allow-tell if you want Bayesian filter learning to be possible ๐:
$ spamd -i localhost:2142 -i /tmp/.spamsock -d [-L] [-l]
$ spamd --listen=localhost:2142 --listen=/tmp/.spamsock \
--daemonize [--local] [--allow-tell]
After that, S-nail can make use of these interfaces โจ:
$ s-nail -Sspam-interface=spamc -Sspam-maxsize=500000 \
-Sspamc-command=/usr/local/bin/spamc \
-Sspamc-arguments="-U /tmp/.spamsock" -Sspamc-user=
or
$ s-nail -Sspam-interface=spamc -Sspam-maxsize=500000 \
-Sspamc-command=/usr/local/bin/spamc \
-Sspamc-arguments="-d localhost -p 2142" -Sspamc-user=
Using the generic filter approach lets you use programs like bogofilter(1) ๐ฆ. Here's an example, and it needs to be accessible via PATH ๐:
$ s-nail -Sspam-interface=filter -Sspam-maxsize=500000 \
-Sspamfilter-ham="bogofilter -n" \
-Sspamfilter-noham="bogofilter -N" \
-Sspamfilter-nospam="bogofilter -S" \
-Sspamfilter-rate="bogofilter -TTu 2>/dev/null" \
-Sspamfilter-spam="bogofilter -s" \
-Sspamfilter-rate-scanscore="1;^(.+)$"
okay bestie, so the local spam check should go last ๐
because your messages have to actually be sitting in local storage before they can get scored (or used to train the Bayesian filter) ๐ง ngl that's just the vibe. You can also have spam checked automatically whenever you open certain folders ๐ฆ by setting a special flavor of the internal variable folder-hook, like this:
define spamdelhook {
# Server side DCC
spamset (header x-dcc-brand-metrics "bulk")
# Server-side spamassassin(1)
spamset (header x-spam-flag "YES")
del :s # TODO we HAVE to be able to do `spamrate :u ! :sS'
move :S +maybe-spam
spamrate :u
del :s
move :S +maybe-spam
}
set folder-hook-SOMEFOLDER=spamdelhook
If you want more spam tea โ check out the docs for spam-interface, spam-maxsize, spamc-command, spamc-arguments, spamc-user, spamfilter-ham, spamfilter-noham, spamfilter-nospam, spamfilter-rate and spamfilter-rate-scanscore. They're literally all in there, iykyk ๐
โจ COMMANDS โจ
okay so S-nail reads input line by line ๐ธ If you end a command line with an unquoted reverse solidus \, it "escapes" the newline. That newline gets tossed, and the next line becomes a follow-up line with all its leading whitespace stripped. Once the whole thing is complete, the whitespace characters space, tabulator and newline (plus anything defined by the variable ifs) get trimmed off the beginning and end ๐ Also, if you put any whitespace at the very start of a line, that command line won't get added to the [Option]al history. Sneaky, but make it make sense ๐ซถ
Next up, the start of the line gets scanned for a known command name. Command names can be abbreviated, and the first command matching your prefix is the one that gets used ๐ง You can put command modifiers in front of a command to change how it behaves. A name can also be a commandalias, which keeps expanding until there's nothing left to expand ๐ฅ Once S-nail knows what command it's running, the rest of the line gets interpreted using that command's own rules, which we'll get into below.
This is lowkey different from the sh(1)ell ๐ The sh(1)ell is a whole programming language with syntax that has clearly defined semantics, so it can expand and evaluate each piece of a line one by one. S-nail doesn't do that. For example, ? set one=value two=$one will never assign value to one, because the assignment only happens inside the command (set), long after the expansion already took place ๐ญ
A list of every command in lookup order is dumped by the command list. You can also use the command help (or ?) with an argument [Option]ally to show a documentation string for the command matching your expanded argument. So ?t works as a shorthand for ?type โจ Both commands support a more verbose listing mode with these doc strings, which includes the argument type and other info that applies. A handy suggestion would be something like this:
? define __xv {
# Before v15: need to enable sh(1)ell-style on _entire_ line!
localopts yes;wysh set verbose;ignerr eval "${@}";return ${?}
}
? commandalias xv '\call __xv'
? xv help set
Command modifiers
Commands can be prefixed by none, or multiple, command modifiers ๐ Some modifiers only work with a restricted set of commands, and the verbose version of list will ([Option])ally show you which modifiers apply to what.
\โ This one has to go first ๐ It stopscommandaliasexpansions on the rest of the line. So\echowill always run the commandecho, even if an alias with that same name exists. The content of acommandaliascan have its own command modifiers too, including a leading\to stop further expansions.ignerrโ This tells the state machine to ignore any error that the following command throws, so it doesn't cause a program exit even witherrexitenabled, or with the standardized exit cases in posix mode ๐ The variable?, one of the "INTERNAL VARIABLES", gets set to the real exit status of the command no matter what.localโ This makes the called command apply its changes only temporarily, local to the block scope ๐ฆ It can only be used inside a defined macro or an account definition. Using it implies the modifierwysh. Local variables don't get inherited by macros deeper in the call chain, and all local settings get garbage collected once you leave the scope. If you want to record and unroll changes in the global scope instead, use the commandlocalopts.scopeโ does yet not implement any functionality ๐uโ does yet not implement any functionality ๐vputโ Some commands support this one ๐ When it's used, the command expects the name of a variable as its first argument, and that name can itself be a variable (shell expansion gets applied). The result gets placed in that variable instead of the default spot, which is usually standard output.- The given name gets tested to make sure it's a valid sh(1) variable name. That means it can only have upper- and lowercase characters, digits, and the underscore. The hyphen-minus can be used as a non-portable extension. Digits can't be the first character, and hyphen-minus can't be the last ๐ญ
- The name must either not be one of the known "INTERNAL VARIABLES", or it has to refer to a writable (non-boolean) value variable.
- The actual put operation can still fail anyway. For example, if the variable expects a number argument, only a number will be accepted ๐ โโ๏ธ
- Any error during these operations makes the command fail, and the error number
!gets set to^ERR-NOTSUP. The exit status?should be set to-1, but some commands deviate from that, and any deviation is documented.
wyshโ Last but not least ๐ This modifier lets some old and established commands use the new "Shell-style argument quoting" rules instead of the traditional "Old-style argument quoting" rules. It's implied ifv15-compatis set to a non-empty value.
Old-style argument quoting
[v15 behaviour may differ] This section covers the traditional, POSIX standardized way of quoting non-message list arguments for commands that expect this kind of argument ๐ Most commands still use it, but the new "Shell-style argument quoting" can be used even for those through wysh, one of the "Command modifiers". Still, be careful, because only the new commands were designed with all the capabilities of the new quoting rules in mind. Those rules can, for example, generate control characters, so be warned ๐
- An argument can be wrapped in paired double-quotes
"argument"or single-quotes'argument'. Any whitespace, shell word expansion, or reverse solidus characters inside the quotes get treated literally as part of the argument (except as described in the next bullet). A double-quote is treated literally inside single-quotes, and vice versa. If you need the quote character you're actually using inside the string, escape it with a reverse solidus\, like"y\"ou"โจ - An argument that isn't in quotes can still have spaces, as long as they're reverse solidus escaped, like
you\ are๐ธ - A reverse solidus outside of the enclosing quotes gets discarded, and the character after it is treated literally as part of the argument ๐ซถ
Shell-style argument quoting
The sh(1)ell-style argument parsing and quoting rules, which are POSIX standardized, are used by most commands. [v15 behaviour may differ] Most new commands only support these new rules and are flagged [Only new quoting rules]. Some elder commands can use them with the command modifier wysh. In the future, only this type of argument quoting will remain ๐ฎ
A command line gets parsed from left to right, and an input token is completed whenever an unquoted, otherwise ignored, metacharacter shows up ๐งฉ The metacharacters are the vertical bar |, ampersand &, semicolon ;, all the characters from the variable ifs, and/or space, tabulator, newline. The extra metacharacters that sh(1) supports, left and right parenthesis (, ) and less-than and greater-than signs <, >, are not used here. They're treated as ordinary characters, because they're a vivid part of email addresses, and it seems highly unlikely that they'll ever become meaningful to S-nail ๐
Compatibility note: [v15 behaviour may differ] Please note that even many new-style commands don't yet honourifsto parse their arguments ๐ The sh(1)ell is a language with syntactic elements that have clearly defined semantics. S-nail, on the other hand, parses entire input lines and decides on a per-command basis what to do with the rest of the line. This also means that whenever an unknown command is seen, all S-nail can do is cancel processing of the remains of the line. It also often depends on an actual subcommand of a multiplexer command how the rest of the line should be treated, and until v15 we aren't capable of this deep inspection of arguments. Nonetheless, at least the following commands, which work with positional parameters, fully supportifsfor an almost shell-compatible field splitting:call,call_if,read,vpospar,xcall๐
Any unquoted number sign # at the beginning of a new token starts a comment that runs to the end of the line, which also ends argument processing ๐ An unquoted dollar sign $ causes variable expansion of the given name. That name must be a valid sh(1)ell-style variable name (see vput). "INTERNAL VARIABLES" as well as "ENVIRONMENT" (shell) variables can be accessed this way, and brace enclosing the name is supported, i.e., to subdivide a token ๐ฆ
Okay so the metacharacters space, tabulator and newline only finish off an input token ๐ง, but the vertical bar |, ampersand & and semicolon ; are the real main characters ๐. They act as control operators and actually perform control functions ๐
. For now only the semicolon ; is supported โจ. It terminates a single command, which sequences the whole command line and makes the rest of the line get reevaluated ๐. That means multiple command argument types and quoting rules can all apply to one single line, and before v15 that could get super problematic ๐ญ. Like, the first of these is going to give you surprising results ๐:
? echo one; set verbose; echo verbose=$verbose.
? echo one; wysh set verbose; echo verbose=$verbose.
Quoting is the mechanism that strips the special meaning from metacharacters and reserved words, and it stops expansion from happening ๐ซถ. There are four quoting mechanisms: the escape character, single-quotes, double-quotes and dollar-single-quotes:
- The literal value of any character can be kept by putting the escape character
\(reverse solidus) right in front of it. - Arguments wrapped in
'single-quotes'keep their literal value. A single-quote cannot occur within single-quotes, so no nesting drama here ๐ โโ๏ธ. - The literal value of all characters inside
"double-quotes"is kept, with a few exceptions ๐:- The dollar sign
$still does variable expansion, as above. - The backquote (grave accent)
`doesn't mean anything special yet. - The reverse solidus
\escapes any of these:$(to prevent variable expansion),`(backquote),"(to prevent ending the quote) and\(to prevent escaping, i.e., to embed a reverse solidus character as-is). Otherwise it has no special meaning.
- The dollar sign
- Arguments enclosed in
$'dollar-single-quotes'act like normal single quotes, except that reverse solidus escape sequences get expanded as follows ๐:\aโ bell control character (ASCII and ISO-10646 BEL).\bโ backspace control character (ASCII and ISO-10646 BS).\Eโ escape control character (ASCII and ISO-10646 ESC).\eโ the same.\fโ form feed control character (ASCII and ISO-10646 FF).\nโ line feed control character (ASCII and ISO-10646 LF).\rโ carriage return control character (ASCII and ISO-10646 CR).\tโ horizontal tabulator control character (ASCII and ISO-10646 HT).\vโ vertical tabulator control character (ASCII and ISO-10646 VT).\\โ emits a reverse solidus character.\'โ single quote.\"โ double quote (escaping is optional).\NNNโ eight-bit byte with the octal valueNNN(one to three octal digits), optionally prefixed by an additional0. A 0 byte will suppress further output for the quoted argument.\xHHโ eight-bit byte with the hexadecimal valueHH(one or two hexadecimal characters, no prefix, see vexpr). A 0 byte will suppress further output for the quoted argument.\UHHHHHHHHโ the Unicode / ISO-10646 character with the hexadecimal codepoint valueHHHHHHHH(one to eight hexadecimal characters) ๐ฆ. Note that Unicode defines the maximum codepoint ever to be supported as0x10FFFF(in planes of0xFFFFcharacters each). This escape is only supported in locales that support Unicode (see โCharacter setsโ). In other cases the sequence will remain unexpanded, unless the given code point is ASCII compatible or (if the [Option]al character set conversion is available) can be represented in the current locale. The character NUL will suppress further output for the quoted argument.\uHHHHโ Identical to\UHHHHHHHHexcept it takes only one to four hexadecimal characters.\cXโ Emits the non-printable (ASCII and compatible) C0 control codes 0 (NUL) to 31 (US), and 127 (DEL). Printable representations of ASCII control codes can be made by mapping them to a different, visible part of the ASCII character set. Adding the number 64 does this for the codes 0 to 31, here 7 (BEL):7 + 64 = 71 = G. The real operation is a bitwise logical XOR with 64 (bit 7 set, see vexpr), which also covers code 127 (DEL), mapped to 63 (question mark):? vexpr ^ 127 64.
Historically circumflex notation was used a lot to visualize control codes, like ^G, but the reverse solidus notation has been standardized: \cG ๐ง. Some control codes also have standardized (ISO-10646, ISO C) aliases, as shown above (\a, \n, \t etc). Whenever such an alias exists, it will be used for display purposes. The control code NUL (\c@, a non-standard extension) will suppress further output for the remains of the token (which may extend beyond the current quote), or, depending on the context, the remains of all arguments for the current command ๐.
\$NAMEโ Non-standard extension: expands the given variable name, as above. Brace enclosing the name is supported.\`{command}โ Not yet supported, just to raise awareness. Non-standard extension.
Caveats, bestie ๐:
? echo 'Quotes '${HOME}' and 'tokens" differ!"# no comment
? echo Quotes ${HOME} and tokens differ! # comment
? echo Don"'"t you worry$'\x21' The sun shines on us. $'\u263A'
๐ Message list arguments
Many commands operate on message list specifications, as documented in โSpecifying messagesโ. The argument input is first split into individual tokens via โShell-style argument quotingโ, which are then interpreted as the mentioned specifications. If no explicit message list has been specified, many commands will search for and use the next message forward that satisfies the commands' requirements. If there are no messages forward of the current message, the search proceeds backwards. If there are no good messages at all to be found, an error message is shown and the command is aborted ๐ญ. The verbose output of the command list will indicate whether a command searches for a default message, or not ๐.
๐ง Raw data arguments for codec commands
A special set of commands, which all have the string โcodecโ in their name, like addrcodec, shcodec, urlcodec, take raw string data as input. This means the content of the command input line is passed completely unexpanded and otherwise unchanged โจ. Like this, the effect of the actual codec is visible without any noise from possible shell quoting rules etc., so you can input one-to-one the desired or questionable data ๐. To gain a level of expansion, the entire command line can be evaluated first, for example:
? vput shcodec res encode /usr/Schรถnes Wetter/heute.txt
? echo $res
$'/usr/Sch\u00F6nes Wetter/heute.txt'
? shcodec d $res
$'/usr/Sch\u00F6nes Wetter/heute.txt'
? eval shcodec d $res
/usr/Schรถnes Wetter/heute.txt
๐ Filename transformations
Filenames, where expected, and unless documented otherwise, are subsequently subject to the following filename transformations, in sequence ๐:
- If the given name is a registered shortcut, it will be replaced with the expanded shortcut. This step is mostly taken for folders only.
- The filename is matched against the following patterns or strings. But for plus
+filefolder expansion, this step is mostly taken for folders only.#(Number sign) is expanded to the previous file.%(Percent sign) is replaced by the invoking user's primary system mailbox. That's either the (itself expandable) inbox if that is set, the standardized absolute pathname indicated byMAILif that is set, or a built-in compile-time default otherwise. When opening a folder, the used name is actively checked for being a primary mailbox, first againstinbox, then againstMAIL๐.%userexpands to the primary system mailbox of user (and never the value ofinbox, regardless of its actual setting).&(Ampersand) is replaced with the invoking user's secondary mailbox, theMBOX.+filerefers to a file in the folder directory (if that variable is set).%:filespecexpands to the same value asfilespec, but it has special meaning when used with, for example, the commandfolder. The file will be treated as a primary system mailbox by, among others, themboxandsavecommands, meaning that messages that have been read in the current session will be moved to theMBOXmailbox instead of simply being flagged as read ๐ .
- Meta expansions may be applied to the resulting filename, as allowed by the operation and applicable to the resulting access protocol (also see โOn URL syntax and credential lookupโ). For the file-protocol, a leading tilde
~character will be replaced by the expansion ofHOME, except when followed by a valid user name. In that case, the home directory of the given user is used instead ๐ธ.
A shell expansion as if you typed it in double-quotes (see โShell-style argument quotingโ) can be applied ๐
. That means any $VARIABLE (or ${VARIABLE}) gets swapped out for the variable's value, if it's possible โจ. โINTERNAL VARIABLESโ and โENVIRONMENTโ (shell) variables are both reachable through this mechanism ๐ธ.
Shell pathname wildcard pattern expansions (glob(7)) can also be applied, as documented. If the fully expanded filename turns into multiple pathnames and the command only wants one file, you get an error ๐.
In interactive context, arguments are usually shown in a properly quoted form so that hitting ENTER just works ๐ซถ. So a file diet\ is \curd.txt might be displayed as 'diet\ is \curd.txt' ๐.
The following commands are available ๐ฆ:
โจ ! โจ
This runs the SHELL command that follows ๐. Unescaped exclamation marks get replaced with the previously executed command if the internal variable bang is set. This command supports vput as documented in โCommand modifiersโ, and it manages the error number !. A 0 or positive exit status ? reflects the exit status of the command. Negative ones mean an error happened before the command was executed, or that the program didn't exit cleanly (maybe because of a signal). In that case the error number is ^ERR-CHILD ๐ญ.
With the vput modifier, a few special cases come into play ๐ง. A negative exit status means the collected data couldn't be stored in the given variable, which is a ^ERR-NOTSUP error that shouldn't really happen. ^ERR-CANCELED means no temporary file could be created to collect the command output at first glance. If there's a catchable out-of-memory situation, you'll get ^ERR-NOMEM, and S-nail will try to store the empty string, just like with all other detected error conditions ๐.
โจ # โจ
The comment command makes the entire line get ignored ๐. Heads up: this is a normal command whose whole purpose is to throw its arguments away. It is not a special โcomment-startโ character. So trailing comments on a line aren't possible, except for commands that use โShell-style argument quotingโ ๐.
โจ + โจ
Goes to the next message in sequence and types it, just like ENTER ๐.
โจ - โจ
Displays the preceding message, or the n'th previous message if you give it a numeric argument n ๐.
โจ = โจ
Without arguments, this shows the message number of the current message (the โdotโ). With arguments, it shows the numbers of the given list instead ๐ฅ. The output numbers get separated from each other with the first character of ifs. They're followed by the first character of if-ws, as long as that isn't empty and isn't identical to the first one. If that results in no separation at all, a space character gets used instead. This command supports vput (see โCommand modifiersโ), and it manages the error number ! ๐.
โจ ? โจ
[Option] This shows a brief summary of commands ๐ธ.
[Option] Give it an argument and you'll see a synopsis for that command instead. Commands can be abbreviated in general, and this command can show the full expansion of an abbreviation, synopsis included. Try ?h, ?hel and ?help and watch how the output changes ๐. To stop aliases from being resolved, you can prepend the modifier \ to the argument. Just note that it must be quoted. This mode also supports a more verbose output, which provides the information documented for list ๐
.
โจ | โจ
A synonym for the pipe command ๐ฆ.
โจ account, unaccount โจ
(ac, una) Creates, selects or lists (an) account(s) ๐. Accounts are special incarnations of defined macros and group commands, plus variable settings, which together usually arrange the environment for creating an email account. Different to normal macro settings, which are covered by localopts (here by default enabled!), accounts won't be reverted before the account is changed again ๐ง. The special account null (case-insensitive) always exists. All but it can be deleted by the latter command, and in one operation with the special name *. Also, for all but it, a possibly set on-account-cleanup hook gets called once they're left, including on program exit โจ.
Without arguments you get a listing of all defined accounts ๐. With one argument, the given account gets activated: its system inbox is activated (as via folder), a possibly installed folder-hook runs, and the internal variable account gets updated ๐. The two-argument form behaves identically to defining a macro with define ๐ซถ. Important settings for accounts include folder, from, hostname, inbox, mta, password and user (โOn URL syntax and credential lookupโ). There are also things like tls-config-pairs (โEncrypted network communicationโ), plus protocol specifics like imap-auth, pop3-auth and smtp-auth ๐ธ.
account myisp {
set folder=~/mail inbox=+syste.mbox record=+sent.mbox
set from='(My Name) myname@myisp.example'
set mta=smtp://mylogin@smtp.myisp.example
}
โจ addrcodec โจ
This performs email address codec transformations on a raw-data argument, rather according to email standards (RFC 5322; [v15 behaviour may differ] will furtherly improve) ๐ฎ. It supports vput (see โCommand modifiersโ), and it manages the error number !. The first argument must be either [+[+[+]]]e[ncode], d[ecode], s[kin] or skinl[ist]. It specifies the operation to perform on the rest of the line ๐.
Decoding shows how a standard-compliant MUA would display the given argument, which should be an email address ๐. Heads up, though: most MUAs have difficulties with address standards ๐ญ. They vary wildly whenever (comments) in parenthesis, โdouble-quotedโ strings, or quoted-pairs (as below) get involved. [v15 behaviour may differ] S-nail currently does not perform decoding when displaying addresses ๐.
Skinning is identical to decoding, but it only outputs the plain address, without any string, comment, etc. components โจ. Another difference: it may fail with the error number ! set to ^ERR-INVAL if decoding can't find a (valid) email address. In that case, the unmodified input is output again ๐.
skinlist first performs a skin operation, and then checks whether a valid address is a registered mailing list (see mlist and mlsubscribe). It eventually reports that state in the error number ! as ^ERR-EXIST. (This state could later get overwritten by an I/O error, though ๐ฅฒ.)
Encoding supports four different modes ๐ฆ. Lesser automated versions can be chosen by prefixing one, two or three plus signs. The standard gives some characters a special meaning. Those have to be transformed into so-called quoted-pairs by pairing them with a reverse solidus \ in order to remove that special meaning. Be warned: this might change the interpretation of the entire argument from what you wanted! ๐ฅ Specify one plus sign to leave parenthesis alone. Use two to stop double quotation marks from turning into quoted-pairs, and three to also leave any user-specified reverse solidus alone ๐
. The result will always be valid if a successful exit status is reported ([v15 behaviour may differ] the current parser fails this assertion for some constructs) ๐. [v15 behaviour may differ] Addresses need to be specified between angle brackets < and > if the construct gets more difficult. Otherwise the current parser will fail, because it is not smart enough to guess right ๐ญ.
? addrc enc "Hey, you",<diet@exam.ple>\ out\ there
"\"Hey, you\", \\ out\\ there" <diet@exam.ple>
? addrc d "\"Hey, you\", \\ out\\ there" <diet@exam.ple>
"Hey, you", \ out\ there <diet@exam.ple>
? addrc s "\"Hey, you\", \\ out\\ there" <diet@exam.ple>
diet@exam.ple
โจ alias, unalias โจ
[Only new quoting rules] (a, una) These define or list (and, respectively, remove) address aliases ๐. Aliases are a method of creating personal distribution lists, mapping a single name to none, one or multiple receivers. They get expanded after โCompose modeโ is left, and the expansion correlates with metoo ๐. The latter command removes all given aliases. The special name asterisk * removes all existing aliases ๐งน. Without arguments, the former shows a list of all currently known aliases. With one argument, it shows only the target(s) of the given one. When given two arguments, with hyphen-minus - being the first, the target(s) of the second get expanded recursively ๐ซถ.
In all other cases the given alias is newly defined, or gets appended to. Arguments must either be valid alias names themselves, or any other address type (see โOn sending mail, and non-interactive modeโ) ๐. Recursive expansion of aliases can be prevented by prefixing the desired argument with the modifier reverse solidus \. A valid alias name conforms to mta-aliases syntax, but follow-up characters can also be the number sign #, colon :, commercial at @, exclamation mark !, period ., as well as โany character that has the high bit setโ ๐. The dollar sign $ may be the last character. The number sign # may need โShell-style argument quotingโ ๐.
[v15 behaviour may differ] okay bestie, the colon is currently not supported ๐ญ because it messes with the normal address parsing rules. [v15 behaviour may differ] high bit characters will probably throw warnings rn for the same reasons colon is unsupported ๐, and later on, locale dependent character set validity checks will be performed ๐.
? alias cohorts bill jkf mark kridle@ucbcory ~/cohorts.mbox
? alias mark mark@exam.ple
? set mta-aliases=/etc/aliases
alternates, unalternates ๐ฆ
[Only new quoting rules] (alt) okay so this one manages a list of alternate addresses or names for the active user ๐
. Anyone on that list gets removed from recipient lists, except for one ๐ซถ. There's a set of implicit alternates that gets built from the values of LOGNAME, from, sender and reply-to ๐. Just know that from won't be used if sender is set. The latter command removes the alternates you give it, and the special name * will nuke every existing alternate name ๐.
The former command manages the error number !. Used with no arguments, it shows the current set of alternates, and only in this mode does it also support vput (see โCommand modifiersโ) โจ. Otherwise, the arguments you give it (after being checked for validity) get appended to the list of alternate names. In posix mode, though, they replace that list instead ๐ธ.
answered, unanswered ๐
Take a message list and mark each message as (not) having been answered โ
. Messages get marked answered automatically when you reply to them, but only if the markanswered variable is set. Check out the section โMessage statesโ for the full tea ๐ต.
bind, unbind ๐
[Option][Only new quoting rules] the bind command lets you extend the MLE (see โOn terminal control and line editorโ) with freely configurable key bindings, and it's giving customization queen ๐. The latter command, unbind, removes the given key binding from the given context. Both arguments can be a wildcard *, so unbind * * will remove all bindings of all contexts ๐ฅ. Heads up though: due to initialization order, unbinding won't work for built-in key bindings at program startup ๐ญ. For that, use line-editor-no-defaults instead ๐ซถ.
With zero arguments, or with just a context name, the former command shows all key bindings. If you pass a context, it shows the bindings for that context only. An asterisk * will iterate over all contexts ๐. You'll get a more verbose listing if either debug or verbose is set ๐ง.
With two or more arguments, you can view a specific binding or (re)establish it. The first argument is the context the binding applies to. The second argument is a comma-separated list of the โkeysโ that form the binding. Any further arguments get joined to form the expansion, and they create or update the binding ๐ฆ. If you want a binding to be editable by the user instead of auto-committed, put a commercial at @ as the last thing in the expansion. That @ gets removed, and leading and trailing whitespace gets trimmed off the expansion too โ๏ธ. Heads up: a reverse solidus can't be the last character of an expansion ๐
โโ๏ธ, and an empty expansion gets rejected ๐ซ.
Contexts decide when a binding applies. A binding won't be seen unless its context is currently active ๐. The shared binding base is the exception: it's the foundation for all other bindings and always applies, but its bindings only apply secondarily ๐. The available contexts are:
baseโ the shared context, the foundation for everything else ๐งฑdefaultโ used in all situations that aren't otherwise documented ๐composeโ applies only to โCompose modeโ โ๏ธ
Bindings are specified as a comma-separated list of byte sequences, where each list entry is one โkeyโ (press) ๐น. Byte sequence boundaries get forcefully terminated after bind-inter-byte-timeout milliseconds, while key sequences can be timed out via bind-inter-key-timeout โฑ๏ธ. A list entry can also refer to the name of a terminal capability if it has a leading colon :. Several dozen names are compiled in, and you can use either their terminfo(5) name or, if one exists, their termcap(5) name. This works no matter which [Option]al terminal control library is actually being used ๐ฅ๏ธ. Any capability works too, as long as the name is resolvable by the [Option]al control library, or was defined via the internal variable termcap ๐
. Input sequences aren't case-normalized, so you need an exact match to update or remove a binding ๐ . It's a good idea to start user bindings (as opposed to purely terminal capability based ones) with an escape or other control character, like โ\cAโ, to avoid ambiguities. It also reduces search time โก. Here are some examples:
? bind base a,b echo one
? bind base $'\E',d mle-snarf-word-fwd # Esc(ape)
? bind base $'\E',$'\c?' mle-snarf-word-bwd # Esc,Delete
? bind default $'\cA',:khome,w 'echo Editable binding@'
? bind default a,b,c rm -irf / @ # Also editable
? bind default :kf1 File %
? bind compose :kf1 ~v
โ ๏ธ bestie, that rm -irf / one is just an example of syntax. Do NOT go running it in your actual shell ๐.
Note that the entire comma-separated list is first parsed (over) as a shell-token, with whitespace as the field separator. Then it gets parsed and expanded for real, with comma as the field separator. So whitespace needs to be properly quoted. See โShell-style argument quotingโ for that ๐ง. Using Unicode reverse solidus escape sequences makes a binding defunctional if the locale doesn't support Unicode (see โCharacter setsโ) ๐ฅฒ. Using terminal capabilities does the same if no (corresponding) terminal control support is (currently) available ๐. Adding, deleting or modifying a key binding invalidates the internal prebuilt lookup tree. It gets recreated as necessary, and this process gets visualized in most verbose as well as in debug mode ๐.
The following terminal capability names are built-in. They can be used in terminfo(5) or, if available, the two-letter termcap(5) notation. See the respective manual for a full list of capabilities ๐. The program infocmp(1) can show all the capabilities of TERM or the given terminal type. Use the -x flag to also show supported (non-standard) extensions ๐.
kbsorkbโ Backspace ๐kdch1orkDโ Delete character ๐๏ธkDCor*4โ shifted variantkelorkEโ Clear to end of line ๐งนkextor@9โ Exit ๐ชkich1orkIโ Insert character โkICor#3โ shifted variantkhomeorkhโ Home ๐kHOMor#2โ shifted variantkendor@7โ End ๐knporkNโ Next page โญ๏ธkpporkPโ Previous page โฎ๏ธkcub1orklโ Left cursor (with more modifiers: see below) โฌ ๏ธkLFTor#4โ shifted variantkcuf1orkrโ Right cursor (ditto) โก๏ธkRITor%iโ shifted variantkcud1orkdโ Down cursor (ditto) โฌ๏ธkDNโ shifted variant (only terminfo)kcuu1orkuโ Up cursor (ditto) โฌ๏ธkUPโ shifted variant (only terminfo)kf0ork0โ Function key 0. Add one for each function key up tokf9andk9, respectively ๐ขkf10ork;โ Function key 10 ๐kf11orF1โ Function key 11. Add one for each function key up tokf19andF9, respectively ๐น
Some terminals support key-modifier combination extensions, like โAlt+Shift+xyโ ๐. Take the delete key, kdch1, for example. In its shifted variant, the name mutates to kDC, and then a number gets appended for each state. Alt is kDC3, Shift+Alt is kDC4, Control is kDC5, Shift+Control is kDC6, Alt+Control is kDC7, and finally Shift+Alt+Control is kDC8 ๐. The left cursor key, kcub1, works the same way: KLFT, KLFT3, KLFT4, KLFT5, KLFT6, KLFT7, KLFT8 โจ.
call ๐
[Only new quoting rules] call calls the given macro. The macro must have been created via define (see there for more), otherwise you'll get an ^ERR-NOENT error ๐ค. Heads up: calling macros recursively will eventually exceed the stack size limit, which causes a hard program abortion ๐. If recursively calling a macro is the last command of the current macro, consider using the command xcall instead. It releases all resources of the current macro first, and only then replaces the current macro with the called one ๐ซถ.
call_if ๐ฆ
Identical to call if the given macro has been created via define. The difference is that call_if doesn't fail nor warn if the macro doesn't exist ๐ฅฐ.
cd ๐ธ
Synonym for chdir ๐
.
certsave ๐
[Option] Only applicable to S/MIME signed messages ๐ฉ. It takes an optional message list and a filename, then saves the certificates contained within the message signatures to the named file. They get saved in both human-readable and PEM format ๐. Later, you can use those certificates to send encrypted messages to the respective message senders, by setting smime-encrypt-USER@HOST variables ๐.
charsetalias, uncharsetalias ๐
[Only new quoting rules] these two manage alias mappings for (conversion of) โCharacter setsโ ๐บ๏ธ. Alias processing isn't performed for โINTERNAL VARIABLESโ, for example charset-8bit. Mappings are ineffective if character set conversion isn't available, which you can check because features won't announce โ,+iconv,โ ๐
โโ๏ธ. Expansion happens recursively for cases where aliases point to other aliases, with a built-in loop limit of 8 ๐.
Okay so, unalias is the bouncer ๐ช: it deletes every alias you hand it, or nukes them all at once if you pass the asterisk * ๐. alias is the info queen ๐. With no args she lists every alias that's defined, with one arg she shows that alias's target, and when you give two args with hyphen-minus - first, the second one gets expanded recursively ๐. Anything else gets treated as pairs of character sets and their target alias name, which creates new aliases or updates existing ones โจ.
๐ธ chdir
[Only new quoting rules] (ch) โ Bestie, this one just takes you back to HOME or wherever you point it ๐ก. Change the working directory to HOME or the given argument. Synonym for cd ๐.
๐งต collapse, uncollapse
This one only works in threaded sort mode ๐งต. It takes a message list and hides all the replies to those messages in header summaries ๐, except for new messages and the โdotโ ๐ฏ. When you display a message that has collapsed replies, all of them get uncollapsed automatically ๐ช. The latter command, uncollapse, undoes the collapsing ๐ซถ.
๐จ colour, uncolour
[Option][Only new quoting rules]
Okay, colour is where you set the vibes for your โColoured displayโ ๐, and uncolour undoes it ๐งน. Without arguments, colour shows every mapping that's currently defined โจ. Otherwise you need a colour type (case-insensitive), and it has to be one of these:
256โ for 256-colour terminals ๐8,ansiorisoโ the standard 8-colour ANSI / ISO 6429 colour palette ๐จ1ormonoโ for monochrome terminals, which only support (some) font attributes ๐ค
Without further arguments, you get the list of all currently defined mappings of the given type. The special all or * also shows all currently defined mappings, no matter the type ๐.
Otherwise, the second argument is the mappable slot ๐ฏ, the third argument is a (comma-separated list of) colour and font attribute specification(s) ๐ , and the optional fourth argument is a precondition ๐ง . Conditioned mappings get tested in (creation) order until a (case-insensitive) match turns up. The default mapping (if one has been established) is only picked as a last resort ๐. The preconditions you can use depend on the mappable slot, and these ones exist:
Mappings starting with mle- are for the [Option]al built-in Mailx-Line-Editor (MLE, see โOn terminal control and line editorโ) ๐๏ธ, and they don't support preconditions.
mle-positionโ the position indicator that shows up when a line can't fit fully on the screen ๐mle-promptโ used for the prompt ๐ฌmle-errorโ the occasional error indicator that gets joined onto the prompt ๐จ [v15 behaviour may differ] Also used for error messages written on standard error.
Mappings starting with sum- are used in header summaries ๐. They all understand the preconditions dot (the current message) and older for elder messages (only honoured in conjunction with datefield-markout-older) ๐ต.
sum-dotmarkโ the โdotmarkโ you can make with the%>or%<formats of the variable headline ๐ฏsum-headerโ the complete header summary line, except the โdotmarkโ and the thread structure ๐sum-threadโ the thread structure you can make with the%iformat of the variable headline ๐งต
Mappings starting with view- are used when you're displaying messages ๐.
view-from_โ for so-calledFrom_lines, which are MBOX file format specific header lines (also see mbox-rfc4155) ๐ฆview-headerโ for header lines ๐จ. You can give a comma-separated list of headers the mapping applies to as a precondition. If [Option]al regular expression support is available, then once any of the โmagic regular expression charactersโ shows up, the precondition gets evaluated as an (extended) one ๐ช.view-msginfoโ for the introductory message info line ๐view-partinfoโ for MIME part info lines ๐งฉ
These (case-insensitive) colour definitions and font attributes are understood, and you can stack multiple of them in a comma-separated list ๐:
ft=โ a font attribute:bold,reverseorunderline๐ช. It's possible (and often applicable) to specify multiple font attributes for a single mapping โจ.fg=โ foreground colour attribute, in this order (numbers 0 - 7):black,red,green,brown,blue,magenta,cyanorwhite๐จ. To use 256-colour mode, you can give a decimal number colour spec in the range 0 to 255, inclusive, which gets interpreted like this:0 - 7โ the standard ISO 6429 colours, as above ๐8 - 15โ high intensity variants of the standard colours โญ16 - 231โ 216 colours in tuples of 6 ๐232 - 255โ grayscale from black to white in 24 steps ๐ฉถ
Here's the script for that, bestie ๐ :
#!/bin/sh -
fg() { printf "\033[38;5;${1}m($1)"; }
bg() { printf "\033[48;5;${1}m($1)"; }
i=0
while [ $i -lt 256 ]; do fg $i; i=$(($i + 1)); done
printf "\033[0m\n"
i=0
while [ $i -lt 256 ]; do bg $i; i=$(($i + 1)); done
printf "\033[0m\n"
bg=โ background colour attribute (seefg=for possible values) ๐ค
uncolour removes the given mapping for the given colour type ๐งน. The special type * selects all types. If the optional precondition argument is given, only the exact tuple of mapping and precondition gets removed. The special name * removes all mappings (no precondition allowed), so uncolour * * removes all established mappings ๐.
๐ช commandalias, uncommandalias
[Only new quoting rules]
commandalias defines or lists command aliases, and uncommandalias removes them ๐ซ. An (command)alias can be used everywhere a normal command can be used, but it always takes precedence ๐. Any arguments you give the command alias get joined onto the alias expansion, and the resulting string forms the command line that's, in effect, executed ๐โโ๏ธ. uncommandalias removes all the given aliases, and the special name asterisk * removes all existing aliases ๐งน. Without arguments, commandalias shows a list of all currently known aliases. With one argument, it shows just the expansion of that one ๐.
With two or more arguments, you define or update a command alias ๐ ๏ธ. The first argument is the name the remaining command line should be accessible under, and the content can be just about anything ๐. An alias can expand to another alias, but to avoid expansion loops, further expansion gets prevented if an alias refers to itself or if an expansion depth limit is reached ๐. Explicit expansion prevention is available via reverse solidus \, one of the โCommand modifiersโ ๐ซ.
? commandalias xx
s-nail: `commandalias': no such alias: xx
? commandalias xx echo hello,
? commandalias xx
commandalias xx 'echo hello,'
? xx
hello,
? xx world
hello, world
๐ Copy (C)
Similar to copy ๐จ, but it copies the messages to a file named after the local part of the sender of the first message, instead of taking a filename argument ๐ชช. outfolder gets inspected to decide on the actual storage location ๐.
๐พ copy (c)
Copy messages to the named file and do not mark them as being saved ๐
. Otherwise identical to save ๐
.
๐งฎ csop
[Only new quoting rules]
csop is a multiplexer command ๐งฐ that gives you C-style string operations on 8-bit bytes. It has no notion of locale settings and character sets, so it effectively assumes ASCII data ๐ค. For numeric and other operations, refer to vexpr ๐ข. vput, one of the โCommand modifiersโ, is supported ๐. The error result is -1 for usage errors and numeric results, and the empty string otherwise โ. Missing data errors, like unsuccessful searches, set the ! error number to ^ERR-NODATA ๐. Where the question mark ? modifier suffix is supported, a case-insensitive (ASCII mapping) operation mode is available ๐ . The keyword case is optional, so find? and find?case are identical ๐.
lengthโ queries the length of the given argument ๐hash,hash32โ calculates a hash value of the given argument ๐. The latter returns a 32-bit result regardless of host environment ๐ป. The?modifier suffix is supported ๐ . These use Chris Torek's hash algorithm, and the resulting hash value is bit mixed as shown by Bret Mulvey ๐งช.findโ searches for the second argument in the first one ๐. If it's found, it shows the resulting 0-based offset ๐. The?modifier suffix is supported ๐ .
๐ substring
okay so substring makes a substring out of its first argument ๐ง. The optional second argument is the 0-based starting offset, and a negative one counts from the end โจ. The optional third argument is the length you want, and a negative length leaves off that many bytes from the end of the original string ๐. By default the whole string is used ๐
. This one tries super hard to work around faulty arguments (turn on set verbose for error logs ๐), but it still reports them through the error number ! as ^ERR-OVERFLOW ๐.
๐งผ trim
trim trims whitespace off both ends of the argument ๐ซง.
๐ธ trim-front
trim-front trims whitespace off the beginning of the argument ๐ท.
๐ trim-end
trim-end trims whitespace off the end of the argument ๐.
๐บ๏ธ cwd
cwd shows the name of the current working directory, as reported by getcwd(3) ๐. It supports vput (see โCommand modifiersโ) โจ, and the return status is tracked via ? ๐ฅ.
๐ Decrypt
Decrypt [Option] is giving the same energy as Copy for unencrypted messages ๐ซถ. Encrypted messages get decrypted first, if possible, and then copied ๐ฆ.
๐ decrypt
decrypt [Option] is identical to copy for unencrypted messages ๐. Encrypted messages are first decrypted, if possible, and then copied ๐.
๐ง define, undefine
The latter, undefine, deletes the given macro ๐๏ธ. The special name * will wipe out all existing macros, so be careful with that one ๐. You can even delete macros from inside running macros, including the macro deleting itself ๐ญ. Without arguments, define prints the current list of macros along with their content ๐. Otherwise it defines a macro, replacing an existing one with the same name where that applies ๐ฅ.
A defined macro can be invoked explicitly with the call, call_if and xcall commands โจ. It can also run implicitly when a macro hook gets triggered, like a folder-hook ๐ฆ. Calling return inside a macro body stops its execution right there ๐.
Temporary macro block-scope variables can be created or deleted with the local command modifier, used together with the commands set and unset, respectively ๐. To make sure changes made to (global) โINTERNAL VARIABLESโ get rolled back, use the localopts command instead ๐. Its covered scope depends on as what the macro is invoked: a normal macro, a folder hook, a hook, or an account switch ๐.
Inside a called macro, the given positional parameters are implicitly local to the macro's scope ๐งธ. You can access them through the variables *, @, # and 1, plus any other positive unsigned decimal number less than or equal to # ๐ข. You can shift them, or completely replace or remove them, etc. with vpospar ๐
. For numeric computation and string evaluations, vexpr is your helpful friend ๐งฎ, and csop offers C-style byte string operations ๐.
define name {
command1
command2
...
commandN
}
define exmac {
echo Parameter 1 of ${#} is ${1}, all: ${*} / ${@}
return 1000 0
}
call exmac Hello macro exmac!
echo ${?}/${!}/${^ERRNAME}
๐๏ธ delete, undelete
(d, u) delete marks the given message list as โdeletedโ, and undelete marks it as not deleted ๐. If you don't give any argument, the usual search for a visible message kicks in, as documented for โMessage list argumentsโ ๐. It only shows the next input prompt if that search fails ๐. Deleted messages won't be saved in the โsecondary mailboxโ MBOX, and they won't be available for most other commands ๐ซ. If the autoprint variable is set, the new โdotโ (or the last message, when restoring) gets automatically typed ๐ฌ. Also see dp, dt โจ.
๐ง digmsg
[Only new quoting rules] Digging information out of messages is possible through digmsg objects ๐ต๏ธโโ๏ธ. You create one for the given message number, and in โCompose modeโ the hyphen-minus - opens the message being composed instead โ๏ธ. If you give a hyphen-minus as the optional third argument, the output goes to the standard output channel rather than getting consumed by the readall (or read and readsh) command(s) ๐ค.
Heads up, bestie: output must be consumed before normal processing can continue โ ๏ธ. For digmsg objects, that means each command output has to be read until the end of file (EOF) state occurs ๐ญ.
You can remove the objects again by giving the same identifier you used for creation ๐๏ธ. Honestly, that step is optional, because objects get automatically closed when the active folder (mailbox) or compose mode is left ๐งน. In all other use cases, the second argument is an object identifier, and the third and all following arguments are interpreted as via ~^ (see โCOMMAND ESCAPESโ) ๐ช.
? vput = msgno; digmsg create $msgno
? digmsg $msgno header list; readall x; echon $x
210 Subject From To Message-ID References In-Reply-To
? digmsg $msgno header show Subject;readall x;echon $x
212 Subject
'Hello, world'
? digmsg remove $msgno
๐ซง discard
(di) Identical to ignore ๐. It's been superseded by the multiplexer headerpick ๐
.
๐ dp, dt
Deletes the given messages and automatically types the new โdotโ if one exists ๐ฌ, regardless of the setting of autoprint ๐ซ.
๐ฆ dotmove
Moves the โdotโ up or down by one message when given a + or - argument, respectively ๐โโ๏ธ.
๐ draft, undraft
Takes message lists and marks each given message as being draft, or as not being draft, respectively ๐๏ธ. This is documented in the section โMessage statesโ ๐.
๐ฃ echo
(ec) [Only new quoting rules] echo prints the given strings, just like the shell utility echo(1) ๐ข. That means โShell-style argument quotingโ expansion is performed, and unlike the otherwise identical echon, a trailing newline gets echoed ๐. vput (as documented in โCommand modifiersโ) is supported โจ, and the error number ! is managed too ๐ข. If data is stored in a variable, the return value reflects the length of the result string on success, and is -1 on error ๐.
Remarks: this command traditionally (in BSD Mail) also performed โFilename transformationsโ ๐ซ . That's standard incompatible and hard to handle, because quoting transformation patterns is not possible ๐. If you need to expand filenames, the subcommand file-expand of vexpr can do it ๐๏ธ.
๐ข echoerr
[Only new quoting rules] Identical to echo, but the message is written to standard error, and prefixed by log-prefix ๐จ. Also see echoerrn ๐. In interactive sessions, the [Option]al message ring queue for errors gets used instead, if it's available and vput was not used ๐.
๐ echon
[Only new quoting rules] Identical to echo, but it doesn't write or store a trailing newline ๐
โโ๏ธ.
๐ข echoerrn
[Only new quoting rules] Identical to echoerr, but it doesn't write or store a trailing newline either ๐ซฅ.
โ๏ธ edit
(e) Points the text EDITOR at each message from the given list, one at a time ๐. Modified contents get discarded unless the writebackedited variable is set ๐๏ธ. They're also not used unless the mailbox can be written to and the editor returns a successful exit status ๐ซถ. Want something more display oriented? Use visual instead ๐ฅ๏ธ.
๐งฉ elif
Part of the if conditional (see there for more), elif, else, endif ๐ซ. If the condition of a preceding if was false, elif checks the following condition and executes the following block if it evaluates true ๐ฏ.
๐ else
(el) Part of the if conditional (see there for more), elif, else, endif ๐ท. If none of the conditions of the preceding if and elif commands was true, the else block gets executed ๐ธ.
๐ endif
(en) Marks the end of an if (see there for more), elif, else, endif conditional execution block ๐.
๐ environ
[Only new quoting rules] There's a strict separation between โINTERNAL VARIABLESโ and the program โENVIRONMENTโ, which gets inherited by child processes ๐ง. Some environment variables are important for program operation, and their purpose is known, so they've been integrated transparently into handling of the internal variables ๐ซถ. You can access them via set and unset ๐
.
To integrate any other environment variable, and/or to export internal variables into the process environment where they normally aren't, you need to establish a link with this command ๐. For example:
environ link PERL5LIB TZ
After that, changing such variables with set will automatically update the environment too โจ. Sufficient system support is provided (it was in BSD as early as 1987, and has been standardized since Y2K) ๐ฆ. Removing such variables with unset will remove them from the environment as well ๐งน. But in any case, the knowledge that they were ever linked gets lost ๐ญ. This implies that localopts may cause loss of such links ๐.
The subcommand unlink removes an existing link without otherwise touching variables ๐. The set and unset subcommands are identical to set and unset, but they additionally update the program environment accordingly ๐. Removing a variable breaks any freely established link ๐.
๐ errors ๐
[Option] okay so this one is optional, and it's giving helpful. Console UIs can scroll error messages by way too fast and/or out of view, so data can also get sent to an error queue that this command manages. show (or no argument at all) displays the queue and clears it, while clear only clears it. โจ Once the queue fills up with errors-limit entries, the eldest entries get dropped. ๐ซถ There are also the variables ^ERRQUEUE-COUNT and ^ERRQUEUE-EXISTS.
๐
eval ๐
[Only new quoting rules] this one is giving main character energy. ๐ It builds a command by sticking all the arguments together, separated by a single space character, and then evaluates the result. It passes through the exit status ? and the error number ! of the evaluated command. Also see call ๐ฆ
define xxx {
echo "xxx arg <$1>"
shift
if $# -gt 0
\xcall xxx "$@"
endif
}
define yyy {
eval "$@ ' ball"
}
call yyy '\call xxx' "b\$'\t'u ' "
call xxx arg <b u>
call xxx arg < >
call xxx arg <ball>
๐ exit ๐
(ex or x) bestie, this is how you leave S-nail ๐ง It exits without changing the active mailbox, and it skips saving messages to the โsecondary mailboxโ MBOX, plus it skips any possibly tracked line editor history-file. ๐ธ A possibly set on-account-cleanup will still get invoked though. The optional status number argument gets passed through to exit(3).
[v15 behaviour may differ] ngl this part is a little messy for now: the given status might get overwritten. Later, that will only happen if a later error needs to be reported onto an otherwise success indicating status. ๐
๐ File ๐
(Fi) Like folder, but it opens the mailbox read-only. ๐
๐ธ file ๐ธ
(fi) See folder.
๐ฆ filetype, unfiletype ๐ฆ
[Only new quoting rules] these two are the dynamic duo of file handler hooks ๐ filetype defines them and lists them, while unfiletype removes them. Hooks give (shell) commands that let S-nail load and save MBOX files from and to files with registered file extensions, just like shown and described for folder. The extensions are used case-insensitively, yet the auto-completion feature of, for example, folder only works case-sensitively. ๐ญ An intermediate temporary file gets used to store the expanded data.
unfiletype removes hooks for all the given extensions, and the asterisk * removes all existing handlers. ๐
When used without arguments, filetype shows a list of every currently defined file hook. With one argument, it shows the expansion of the given alias. Otherwise it expects three arguments: the first is the file extension the hook is meant for, and the second and third define the load and save commands for that file type. Both of those must read from standard input and write to standard output. ๐
Changing hooks won't affect already opened mailboxes ([v15 behaviour may differ] except below). [v15 behaviour may differ] For now, too much work gets done, and files are often read in twice when once would be enough. This can cause problems if a filetype is changed while such a file is open. It was already like this with the built-in support of .gz etc. in Heirloom, and it will vanish in v15. โจ
[v15 behaviour may differ] For now, all handler strings get passed to the SHELL for evaluation. In the future, a ! prefix on load and save commands may mean bypassing this shell instance. Placing a leading space avoids any possible misinterpretations. ๐ซถ
? filetype bz2 'bzip2 -dc' 'bzip2 -zc' \
gz 'gzip -dc' 'gzip -c' xz 'xz -dc' 'xz -zc' \
zst 'zstd -dc' 'zstd -19 -zc' \
zst.pgp 'gpg -d | zstd -dc' 'zstd -19 -zc | gpg -e'
? set record=+sent.zst.pgp
๐ณ๏ธโ๐ flag, unflag ๐ณ๏ธโ๐
These take message lists and mark the messages as flagged, or as not flagged, respectively, for urgent or special attention. ๐ฅ See the section โMessage statesโ.
๐ง Folder ๐ง
(Fold) Like folder, but it opens the mailbox read-only. ๐
๐ folder ๐
(fold) This is the one for opening a new mailbox, or checking the status info of the current one โจ When you give it an argument, changes (like deletions) get written out first, then a new mailbox gets opened. The internal variables mailbox-resolved and mailbox-display get updated, a matching folder-hook runs, and if the variable header is set, you may also get a summary of headers displayed. ๐
โFilename transformationsโ get applied to the name argument. protocol:// prefixes are understood, since that's URL syntax (see โOn URL syntax and credential lookupโ), like in mbox:///tmp/somefolder. If a protocol prefix is used, the mailbox type is fixated. Otherwise, opening non-existing folders uses the protocol defined in newfolders. ๐ฆ
For the protocols mbox and file (MBOX database), plus eml (electronic mail message, [v15 behaviour may differ] read-only), the list of all registered filetypes gets traversed. This checks whether hooks should be used to load (and save) data from (and to) the given name. Changing hooks won't affect already opened mailboxes. For example, the following creates hooks for the gzip(1) compression tool and a combined compressed and encrypted format:
? filetype \
gzip 'gzip -dc' 'gzip -c' \
zst.pgp 'gpg -d | zstd -dc' 'zstd -19 -zc | gpg -e'
For historic reasons, filetypes provide limited (case-sensitive) auto-completion capabilities. For example, mbox.gz will be found for ? file mbox, provided that corresponding handlers are installed. ๐ It won't find mbox.GZ or mbox.Gz, however, but an explicit ? file mbox.GZ will find and use the handler for gz. [v15 behaviour may differ] The latter mode can only be used for MBOX files. ๐
EML files consist of only one mail message. [v15 behaviour may differ] They can only be opened read-only. When reading MBOX files, tolerant POSIX rules are used by default. Invalid message boundaries, which are pretty common in historic MBOX files, will be complained about (even more with debug). In this case, the method described for mbox-rfc4155 can be used to create a valid MBOX database from the invalid input. ๐ซถ
MBOX databases and EML files will always be protected via file-region locks (fcntl(2)) during file operations, so nothing gets messed up by concurrent modifications. ๐ [Option] An MBOX inbox (MAIL) or โprimary system mailboxโ will also be protected by so-called dotlock files, the traditional way of mail spool file locking. For any file x, a lock file x.lock will be created during the synchronization, in the same directory and with the same user and group identities as the file of interest. This is done as necessary by an external privileged dotlock helper. dotlock-disable disables dotlock files. Also see โFAQโ: โHowto handle stale dotlock filesโ. ๐ธ
[Option] If no protocol has been fixated, and the name refers to a directory with the subdirectories tmp, new and cur, then it gets treated as a folder in โMaildirโ format. The maildir format stores each message in its own file, and it was designed so that file locking isn't necessary when reading or writing files. ๐
[Option] URLs can be used to access network resources, and securely via โEncrypted network communicationโ, if that's supported. Network communication socket timeouts are configurable via socket-connect-timeout. All network traffic may be proxied over a SOCKS server via socks-proxy. ๐
[v15-compat]
protocol://[user[:password]@]host[:port][/path]
[no v15-compat] protocol://[user@]host[:port][/path]
[Option] The supported network protocols are pop3 (POP3) and pop3s (POP3 with TLS encrypted transport), plus imap and imaps. The [/path] part is only valid for IMAP, and there it defaults to INBOX. Network URLs require a special encoding, as documented in the section โOn URL syntax and credential lookupโ. ๐ฅ
๐ folders ๐
Lists the names of all folders below the given argument or folder. โจ For file-based protocols, LISTER gets used for display purposes.
๐ Followup, followup ๐
(Compose mode) (F,fo) This one is similar to Reply and reply, respectively, but it saves the message in a file named after the local part of the (first) recipient's address. It might overwrite record, and it honours outfolder. Also see Copy and Save. ๐ซถ
๐ Forward ๐
(Compose mode) Similar to forward, but it saves the message in a file named after the local part of the recipient's address (instead of in record). ๐ฆ
๐ forward ๐
(Compose mode) Take a message list and the address of a recipient, subject to fullnames, and those messages get sent to them. The text of the original message is included in the new one, enclosed by the values of forward-inject-head and forward-inject-tail. content-description-forwarded-message is inspected. The list of included headers can be filtered with the โforwardโ slot of the white- and blacklisting command headerpick. Only the first part of a multipart message is included, except for forward-as-attachment. ๐
This may generate the errors ^ERR-DESTADDRREQ if no receiver has been specified, or if it was rejected by the expandaddr policy. โ ๏ธ It may also generate ^ERR-IO if an I/O error occurs, ^ERR-NOTSUP if a necessary character set conversion fails, and ^ERR-INVAL for other errors. It can also fail with errors from โSpecifying messagesโ. Any error stops processing of further messages. ๐ญ
from(f) โ okay bestie, this one takes a list of message specifications and shows you a summary of their headers, exactly asheaderswould ๐ It then makes the first message in the result the new โdotโ (or the last message ifshowlastis set) โจsearchis an alias of this command too, iykyk ๐ฆ Also see โSpecifying messagesโ ๐ธFwdโ [Obsolete] alias forForward. It's giving retired ๐fwdโ [Obsolete] alias forforward๐ฅfwdignoreโ [Obsolete] superseded by the multiplexerheaderpick๐fwdretainโ [Obsolete] superseded by the multiplexerheaderpick๐ghost,unghostโ [Obsolete] replaced bycommandaliasanduncommandalias๐งheaderpick,unheaderpickโ [Only new quoting rules] this is a multiplexer command for managing white- and blacklisting selections of header fields across a bunch of different applications ๐ Without arguments, it displays the contexts that have settings. With arguments, the first one is the context the command applies to, and it has to be one of these (case-insensitive):typeโ for display purposes, for exampletypesaveโ selects which headers get stored persistently when yousave,copy,moveor even decrypt messages (heads up: MIME related etc. header fields should not be ignored here, or you'll destroy the message's usability ๐ญ)forwardโ strips messages down when you forward them (no effect ifforward-as-attachmentis set)topโ a user-defined set of fields for the commandtop
The current settings of the given context get displayed if it's the only argument. A second argument picks the type of restriction, and it can be (a case-insensitive prefix of) retain or ignore for white- and blacklisting purposes, respectively ๐ฆ Establishing a whitelist suppresses inspection of the corresponding blacklist.
If no further argument is given, the current settings of the given type get displayed. Otherwise the remaining arguments are header fields to add to the given type, and they [Option]ally may be given as regular expressions. The special wildcard field (asterisk, *) sets up a (fast) shorthand setting that covers all fields ๐
The latter command (unheaderpick) always takes three or more arguments and is how you remove selections. From the given context and the given type of list, all the given headers get removed, and the special argument * removes all headers ๐
headers(h) โ shows the current group of headers. How many you see depends on thescreenvariable in interactive mode, and the format can be defined withheadline๐ If a message-specification is given, the group of headers containing the first message in it is shown, and the message at the top of the screen becomes the new โdotโ. The last message is targeted ifshowlastis set โจhelp(hel) โ a synonym for?๐ซถhistory[Option] โ without arguments, or when given, all history entries are shown (this mode also supports a more verbose output) ๐ The arguments work like this:loadโ replaces the list of entries with the content ofhistory-filesaveโ dumps all entries to said file, replacing former contentclearโ deletes all entriesNUMBERโ a signed decimal number that selects and evaluates the respective history entry, moving it to the top of the history. A negative number is used as an offset to the current command, so-1selects the last command, the history topdeleteโ deletes all given entries (:NUMBER:)
Also see โOn terminal control and line editorโ ๐ง
hold(ho, alsopreserve) โ takes a message list and marks each message in it to be saved in the user's system inbox instead of in the โsecondary mailboxโ MBOX ๐ฅ It does not override thedeletecommand. Heads up: S-nail deviates from the POSIX standard with this command, because anextcommand issued afterholdwill display the following message, not the current one ๐if(i) โ part of theif,elif,else,endifconditional execution construct ๐ If the given condition is true, then the encapsulated block is executed. The POSIX standard only supports the (case-insensitive) conditions โrโeceive and โsโend, and the remaining ones are non-portable extensions. [v15 behaviour may differ] In conjunction with thewyshcommand prefix(es) โShell-style argument quotingโ and more test operators are available โจ
if receive
commands ...
else
commands ...
endif
Further (case-insensitive) one-argument conditions are โtโerminal, which evaluates to true in interactive terminal sessions (running with standard input or standard output attached to a terminal, and none of the โquickrunโ command line options -e, -H and -L have been used). Any boolean value also works (see โINTERNAL VARIABLESโ for textual boolean representations) to mark an enwrapped block as โnever executeโ or โalways executeโ ๐ (Remarks: condition syntax errors skip all branches until endif.) ๐
[no v15-compat] and without wysh: it's possible to check โINTERNAL VARIABLESโ as well as โENVIRONMENTโ variables for existence, or compare their expansion against a user given value or another variable, by using the $ (โvariable nextโ) conditional trigger character. A variable on the right hand side may be signalled using the same mechanism. Variable names may be enclosed in a pair of matching braces. When this mode has been triggered, several operators are available ([v15-compat] and wysh: they are always available, and there is no trigger, because variables will have been expanded by the shell-compatible parser before the if etc. command sees them) ๐ธ
[v15-compat] Two argument conditions. Variables can be tested for existence and expansion. -N will test whether the given variable exists, so -N editalong evaluates to true when editalong is set, whereas -Z editalong will if it is not. -n "$editalong" will be true if the variable is set and expands to a non-empty string, and -z $'\$editalong' only if the expansion is empty, whether the variable exists or not. The remaining conditions take three arguments ๐ง
Integer operators treat the arguments on the left and right hand side of the operator as integral numbers and compare them arithmetically. It is an error if any of the operands is not a valid integer. An empty argument (which implies it had been quoted) is treated as if it were 0. Via the question mark ? modifier suffix, a saturated operation mode is available, where numbers linger at the minimum or maximum possible value instead of overflowing (or trapping). The keyword saturated is optional, so ==?, ==?satu and ==?saturated are therefore identical. Available operators are -lt (less than), -le (less than or equal to), -eq (equal), -ne (not equal), -ge (greater than or equal to), and -gt (greater than) ๐ฅ
String and regular expression data operators compare the left and right hand side according to their textual content. Unset variables are treated as the empty string. Via the question mark ? modifier suffix, a case-insensitive operation mode is available. The keyword case is optional, so ==? and ==?case are identical ๐
Available string operators are < (less than), <= (less than or equal to), == (equal), != (not equal), >= (greater than or equal to), > (greater than), =% (is substring of) and !% (is not substring of). By default these operators work on bytes and (therefore) do not take into account character set specifics. If the case-insensitivity modifier has been used, case is ignored according to the rules of the US-ASCII encoding, i.e., bytes are still compared ๐ซถ
When the [Option]al regular expression support is available, the additional string operators =~ and !~ can be used. They treat the right hand side as an extended regular expression that is matched according to the active locale (see โCharacter setsโ), so character sets should be honoured correctly ๐ฆ
Conditions can be joined via AND-OR lists (where the AND operator is && and the OR operator is ||). These have equal precedence and are evaluated with left associativity, thus using the same syntax that is known from sh(1). It is also possible to form groups of conditions and lists by enclosing them in pairs of brackets [ ... ], which may be interlocked within each other and also be joined via AND-OR lists ๐
The results of individual conditions and entire groups may be modified via unary operators. The unary operator ! will reverse the result โจ
wysh set v15-compat=yes # with value: automatic "wysh"!
if -N debug;echo *debug* set;else;echo not;endif
if "$ttycharset" == UTF-8 || "$ttycharset" ==?cas UTF8
echo ttycharset is UTF-8, the former case-sensitive!
endif
set t1=one t2=one
if [ "${t1}" == "${t2}" ]
echo These two variables are equal
endif
if "$features" =% ,+regex, && "$TERM" =~?case ^xterm.*
echo ..in an X terminal
endif
if [ [ true ] && [ [ "${debug}" != '' ] || \
[ "$verbose" != '' ] ] ]
echo Noisy, noisy
endif
if true && [ -n "$debug" || -n "${verbose}" ]
echo Left associativity, as is known from the shell
endif
ignore (ig) ๐
Okay so ignore is basically the exact same vibe as discard. Iykyk. BUT it's been superseded by the multiplexer headerpick, so it's kinda retired now ๐ฅ
list ๐
list shows you the names of every available command, sorted in command lookup order. Slay. โจ [Option] If you set the variable verbose, you get extra deets for each command: the argument type, the documentation string, and a set of command flags. Here's what the flags mean:
localโ the command supports the command modifierlocal.vputโ the command supports the command modifiervput.*!*โ the error number gets tracked in!.needs-boxโ whether the command needs an active mailbox, a folder.ok:โ indicators for whether the command is ...batch/interactiveโ usable in interactive or batch mode (-#).send-modeโ usable in send mode.subprocessโ allowed to be used when running in a subprocess instance, for example from within a macro that is called viaon-compose-splice.
not ok:โ indicators for whether the command is not ...compose modeโ available in "Compose mode".startupโ available during program startup, like in "Resource files".
gabbyโ the command produces history-gabby history entries. ๐
localopts ๐ธ
Okay this one is giving scope. localopts enforces change localization of environ (linked) "ENVIRONMENT" as well as (global) "INTERNAL VARIABLES", which means their state gets reverted to the former one once the "covered scope" is left. ๐ฆ Just like the command modifier local, which gives block-scope localization for some commands (instead), it can only be used inside macro definition blocks introduced by account or define. The covered scope of an account is left once a different account is activated. Some macros, notably folder-hooks, have their own specific notion of covered scope, and here it gets extended until the folder is left again.
This setting stacks up! If macro1 enables change localization and calls macro2, which explicitly resets localization, then any value changes within macro2 will still be reverted when the scope of macro1 is left. โญ (Caveats: in this example, if macro2 changes to a different account which sets some variables that are already covered by localizations, their scope gets extended. And leaving the account will then restore settings in (likely) global scope which were actually defined in a local, macro private context! ๐ญ)
This command takes one or two arguments. The optional first one specifies an attribute, which can be one of:
scopeโ refers to the current scope and is thus the default.callโ causes any macro that is being called to start with localization enabled by default.call-fixateโ (if enabled) disallows any called macro from turning off localization. This way you can make sure that once the current scope regains control, any changes made in deeper levels have been reverted.
The latter two are mutually exclusive, and neither affects xcall. The (second) argument is interpreted as a boolean (string, see "INTERNAL VARIABLES") and states whether the given attribute shall be turned on or off.
define temporary_settings {
set possibly_global_option1
localopts on
set localized_option1
set localized_option2
localopts scope off
set possibly_global_option2
}
Lfollowup, Lreply ๐
In Compose mode, these reply to messages that come in via known (mlist) or subscribed (mlsubscribe) mailing lists, or pretend to do so (see "Mailing lists"). On top of the usual followup and reply, respectively, they'll actively sort and even remove message recipients so you end up with a message that's meant to go to a mailing list. It's wild, but it will also implicitly generate a Mail-Followup-To: header if that seems useful, no matter what the variable followup-to is set to. For more documentation, check out "On sending mail, and non-interactive mode". ๐ฌ
This may generate the errors ^ERR-DESTADDRREQ if no receiver has been specified, ^ERR-PERM if some addressees were rejected by expandaddr, ^ERR-IO if an I/O error occurs, ^ERR-NOTSUP if a necessary character set conversion fails, and ^ERR-INVAL for other errors. It can also fail with errors from "Specifying messages". Some of these errors depend on the value of expandaddr. Any error stops processing of further messages. ๐
Mail ๐
In Compose mode, this is similar to mail, but it saves the message in a file named after the local part of the first recipient's address (instead of in record). Cute, right? ๐ง
mail (m) ๐
In Compose mode, you can type mail or just m. It takes a (list of) recipient address(es) as (an) argument(s), or asks on standard input if none were given. Then it collects the remaining mail content and sends it out. ๐ Unless the internal variable fullnames is set, recipient addresses get stripped from comments, names, etc. For more documentation, check out "On sending mail, and non-interactive mode".
This may generate the errors ^ERR-DESTADDRREQ if no receiver has been specified, ^ERR-PERM if some addressees were rejected by expandaddr, ^ERR-NOTSUP if multiple messages have been specified, ^ERR-IO if an I/O error occurs, ^ERR-NOTSUP if a necessary character set conversion fails, and ^ERR-INVAL for other errors. It can also fail with errors from "Specifying messages". Some of these errors depend on the value of expandaddr. ๐
mailcap โจ
[Option] If you use mailcap without arguments, or if show has been given, you'll see the content of the "The Mailcap files" cache. It (re-)initializes the cache first, as necessary. If the argument is load, the cache will only be (re-)initialized. If the argument is clear, the contents get removed. ๐งน
Note that S-nail will try to load the files only once, so use mailcap clear to unlock further attempts. Loading and parsing can be made more verbose. ๐ซถ
mbox (mb) ๐ฆ
The given message list is sent to the "secondary mailbox" MBOX when S-nail is quit. This is the default action unless the variable hold is set. [v15 behaviour may differ] This command can only be used in a "primary system mailbox". ๐
mimetype, unmimetype ๐
[Only new quoting rules] Without arguments, mimetype displays the content of the MIME type cache. You get a more verbose listing if either debug or verbose is set. When given arguments, they get joined and interpreted as shown in "The mime.types files" (also see "HTML mail and MIME attachments"). The resulting entry is then added (prepended) to the cache. In any event, MIME type sources are loaded first as necessary. You can use mimetypes-load-control to fine-tune which sources are actually loaded. ๐ฅ
The latter command, unmimetype, deletes all specifications of the given MIME type. So ? unmimetype text/plain will remove all registered specifications for the MIME type text/plain. The special name * discards all existing MIME types, just like reset does. But * also re-enables cache initialization via mimetypes-load-control. ๐ฆ
mimeview ๐ฌ
[v15 behaviour may differ] This one is only available in interactive mode. It allows execution of external MIME type handlers which don't integrate into the normal type output (see "HTML mail and MIME attachments"). [v15 behaviour may differ] There's no syntax to directly address parts, but this restriction may vanish. ๐ค The user gets asked, for each non-text part of the given message in turn, whether the registered handler should be used to display the part.
mlist, unmlist ๐
[Only new quoting rules] These manage the list of known "Mailing lists". Subscriptions are controlled via mlsubscribe. The latter command, unmlist, deletes all given arguments, or all at once when given the asterisk *. The former, mlist, shows the list of all currently known lists if used without arguments. Otherwise, the given arguments become known. [Option] In the latter case, arguments which contain any of the "magic regular expression characters" get interpreted as one, possibly matching many addresses. These are sequentially matched via linked lists instead of being looked up in a dictionary. ๐ธ
mlsubscribe,unmlsubscribe: okay, this pair is the bestie ofmlistandunmlist๐ but it only touches the subscription attribute of mailing lists, nothing else ๐ซถ (andmlsubscribewill also create mailing lists that don't exist yet โจ).Move: same energy asmove๐ฆ, except you don't get to name the file. It's named after the local part of the first message's sender, andoutfoldergets a say in where it actually lands ๐move: acts likecopybut then marks the messages for deletion, as long as they transferred successfully ๐More: likemorebut extra โจ. It also shows header fields that wouldn't survive theheaderpickselection, plus all MIME parts. Identical toPage๐more: hands the given messages to thePAGER, even in non-interactive mode, as long as standard output is a terminal ๐ธ Identical topage.mtaaliases[Option]: no args, orshow, and you get themta-aliasescache contents, re-initialized first if needed ๐ง Useloadto only (re-)initialize it, orclearto wipe its contents ๐ซงnetrc[Option]: no args, orshow, displays the~/.netrccache and initializes it if necessary ๐loadwill (re)load it,clearremoves it, and loading and parsing can be made more verbose ๐ฃlookupsearches the cache for the URL you give as the second argument ([USER@]HOST). Seenetrc-lookup,netrc-pipe, the section โOn URL syntax and credential lookupโ, and the section โThe .netrc fileโ for the full file format breakdown ๐newmail: checks the current folder for new mail without committing any changes first ๐ฌ If there's new mail, you'll get a message. If theheadervariable is set, the headers of each new message show up too. This command is not available for all mailbox types โ ๏ธnext(n, like+or โENTERโ): goes to the next message in sequence and types it ๐โโ๏ธ With an argument list, it types the next matching message.New: same asUnread๐new: same asunread๐noop: if the current folder is accessed over a network connection, aNOOPcommand gets sent. Otherwise, nothing happens ๐ดPage: likepage, but also shows header fields that wouldn't pass theheaderpickselection, and all MIME parts ๐ Identical toMore.page: hands the given messages to thePAGER, even in non-interactive mode, as long as standard output is a terminal ๐ท Identical tomore.Pipe: likepipe, but it also pipes header fields that wouldn't pass theheaderpickselection, plus all parts ofmultipart/alternativemessages ๐ชpipe(pi): takes an optional message list and a shell command (which defaults tocmd), then pipes the messages through that command ๐ฐ If thepagevariable is set, every message gets a formfeed character after it ๐preserve(pre): a synonym forhold๐ซPrint(P): alias forType๐print(p): the Research Unix equivalent oftype๐ฆquit(q): ends the session ๐ช Every undeleted, unsaved message goes into the current โsecondary mailboxโMBOX. Messages marked withholdorpreserve, or never referenced, stay in the system inbox. Everything else gets removed from the โprimary system mailboxโ ๐๏ธ If new mail showed up during the session, you'll see โYou have new mailโ ๐จ If you gave it while editing a mailbox file with the command line option-f, the edit file gets rewritten ๐ You return to the shell, unless rewriting the edit file fails. In that case you can escape with theexitcommand ๐ The optional status number argument gets passed through toexit(3). [v15 behaviour may differ] For now, the status you give might get overwritten. Later, that will only happen when a later error needs reporting over an otherwise successful status ๐ซread[Only new quoting rules]: reads a line from standard input, or from the channel set active viareadctl, then assigns the data to the variables you list ๐ง The data gets split as indicated byifs. Variable names are checked by the same rules as documented forvput, and you'll see the same error codes in!. The exit status?tells you how many bytes were read. It'll be-1with the error number!set to^ERR-BADFon I/O errors, or^ERR-NONEat End-Of-File ๐ If there are more fields than variables, the successive fields go into the last variable you gave. If there are fewer fields than variables, the leftover variables get the empty string ๐ซง
? read a b c
H e l l o
? echo "<$a> <$b> <$c>"
<H> <e> <l l o>
? wysh set ifs=:; read a b c;unset ifs
hey2.0,:"'you ",:world!:mars.:
? echo $?/$^ERRNAME / <$a><$b><$c>
0/NONE / <hey2.0,><"'you ",><world!:mars.:><><>
readsh[Only new quoting rules]: likeread, but it splits on shell token boundaries (see โShell-style argument quotingโ) instead of atifs๐ [v15 behaviour may differ] This could become acommandalias, mayberead --tokenize --๐ฎreadall[Only new quoting rules]: reads everything from standard input, or from the channel set active viareadctl, and assigns it to the given variable ๐ The variable name follows the same rules asvput, and the same error codes show up in!. The exit status?gives the bytes read. It's-1with!set to^ERR-BADFon I/O errors, or^ERR-NONEat End-Of-File ๐ซถ [v15 behaviour may differ] Input data length is capped at 31-bits, so be aware ๐readctl[Only new quoting rules]: manages input channels forread,readshandreadall๐๏ธ It exists so you don't have to write messy code, like callingreadfrom inside a macro in non-interactive mode. With no arguments, or when the first argument isshow, you get a listing of all known channels ๐ Otherwise, you can create channels, set existing ones active, and remove them, by giving the string you used to create them ๐ง
The channel name should be a file descriptor number. If parsing the number fails, it's treated as an input file name that goes through minimal โFilename transformationsโ (no meta expansion is done) ๐๏ธ For example (this one needs a modern shell ๐ ):
$ printf 'echon "hey, "\nread a\nyou\necho $a' |\
s-nail -R#
hey, you
$ LC_ALL=C printf 'echon "hey, "\nread a\necho $a' |\
LC_ALL=C 6<<< 'you' s-nail -R#X'readctl create 6'
hey, you
remove[Only new quoting rules]: removes the named files or directories ๐งน If a name points at a mailbox (say, a Maildir mailbox), a mailbox type specific removal happens and the whole mailbox gets deleted ๐ In interactive mode, the user gets asked to confirm first ๐โโ๏ธrename[Only new quoting rules]: takes the name of an existing folder and a new name, then renames the first to the second ๐ โFilename transformationsโ, including shell pathname wildcard pattern expansions (glob(7)), get applied to both arguments ๐ค Both folders have to be the same type ๐ซถReply,Respond(Compose mode) (R): identical toreply, except it only replies to the sender of each message in the list ๐ It uses the first message as the template to quote, for things likeSubject:๐ Settingfliprswaps this command withreply๐reply,respond(Compose mode) (r): takes a message (or list) and group-responds to each one in turn, addressing the sender and all recipients ๐ This is subject tofullnamesandalternatesprocessing.followup-to,followup-to-honour,reply-to-honour, andrecipients-in-ccall shape how it behaves ๐งฉquoteandquote-as-attachmentdecide whether the message you're responding to gets quoted, andcontent-description-quote-attachmentmay come into play ๐ชถ Settingfliprswaps this command withReply๐ The commandLreplyhas special support for replying to mailing lists ๐ฃ For more, check out โOn sending mail, and non-interactive modeโ ๐
This may generate the errors ^ERR-DESTADDRREQ if no receiver has been specified or the receiver was rejected by expandaddr policy, ^ERR-IO if an I/O error occurs, ^ERR-NOTSUP if a necessary character set conversion fails, and ^ERR-INVAL for other errors ๐จ It can also fail with errors from โSpecifying messagesโ. Any error stops processing of further messages ๐
Resend: likeresend, but it doesn't add any header lines ๐ฎ This isn't a way to hide who sent it. It's just useful for sending the same message again to the same recipients ๐resend: takes a list of messages and a name, then sends each message to the given addressee, which is subject tofullnames๐คResent-From:and related header fields get prepended to the new copy of the message ๐ช Saving inrecordonly happens ifrecord-resentis set. [v15 behaviour may differ] (Compose mode) is not entered. The only supported hooks areon-resend-enterandon-resend-cleanup๐ช
This may generate the errors ^ERR-DESTADDRREQ if no receiver has been specified or the receiver was rejected by expandaddr policy, ^ERR-IO if an I/O error occurs, ^ERR-NOTSUP if a necessary character set conversion fails, and ^ERR-INVAL for other errors ๐จ It can also fail with errors from โSpecifying messagesโ. Any error stops processing of further messages ๐
retain(ret) โ Bestie, this one's retired ๐ It got replaced by the multiplexerheaderpick๐returnโ Only works inside a defined macro or an account โจ It hands control of execution back to the outer scope ๐ฆ It takes two optional positive decimal numbers, both defaulting to0. The first is the 32-bit return value, stored in?[v15 behaviour may differ] and later extended to 64-bit ๐ The second is the 32-bit error number, stored in!๐ซถ As documented for?, a non-0exit status may cause the program to exit ๐ญ Be careful with that one, bestie!Save(S) โ It's basicallysave's cousin ๐ but instead of asking you for a filename, it saves the messages into a file named after the local part of the first message's sender ๐ธoutfoldergets a look to decide where the file actually lives ๐๏ธsave(s) โ Takes a message list and a filename, then tacks each message onto the end of that file ๐ "Filename transformations", including shell pathname wildcard pattern expansions (glob(7)), get applied to the filename ๐ง No filename? No stress, it goes into the "secondary mailbox"MBOX๐ฌ The quoted filename plus the generated character count gets echoed on your terminal ๐ If you're editing a "primary system mailbox", the messages get marked for deletion ๐๏ธ Want to filter which saved header fields make it in? Use thesaveslot of the white- and blacklisting commandheaderpick๐ Also seeCopy๐savediscardโ [Obsolete] Superseded by the multiplexerheaderpick๐saveignoreโ [Obsolete] Superseded by the multiplexerheaderpick๐saveretainโ [Obsolete] Superseded by the multiplexerheaderpick๐searchโ Takes a message specification (list) and shows a header summary of every matching message, just likeheadersdoes ๐ It's an alias offromโจ Also see "Specifying messages" ๐ฆseenโ Takes a message list and marks all of them as read ๐ Done and dusted ๐set,unset(se, [Only new quoting rules]uns) โ Okay, sounsetis the one that deletes all the global variables you name ๐งน Or, if you used thelocalmodifier, it only nukes the block-scope local ones ๐setwith no arguments shows every variable it knows about, and gets extra chatty ifdebugorverboseis set ๐ฃ Real talk though: this list mode won't automatically link in (known) "ENVIRONMENT" variables ๐ That only happens with explicit addressing. Thinkvarshow, using a variable in anifcondition or a string passed toecho, explicit setting, and some program-internal look-ups ๐
Give it arguments and it sets or adjusts the variables. Arguments look like name=value (no space before or after the =), or just a plain name if there's no value, which means it's a boolean variable ๐ If a name starts with no, like set nosave, it's the same as unset on the rest of the name (unset save) ๐ซถ [v15 behaviour may differ] With the wysh (or local) command prefix(es), "Shell-style argument quoting" can quote arguments whenever you need it ๐ [v15 behaviour may differ] Otherwise, you can put quotation marks around any part of the assignment to quote blanks or tabs โจ
In global scope, any name that maps to an environment variable will automatically update the program environment ๐ (Unsetting a variable in the environment needs corresponding system support, fyi ๐ค) For further environment control, use the command environ ๐ง If you use the local command modifier to enforce local scoping, the user variables get garbage collected when the local scope is left ๐๏ธ "INTERNAL VARIABLES" are a different story though: local acts like localopts was set (temporarily), so changes get inherited by deeper scopes ๐ฆ Also see varshow and the sections "INTERNAL VARIABLES" and "ENVIRONMENT" ๐
? wysh set indentprefix=' -> '
? wysh set atab=$'' aspace=' ' zero=0
shcodecโ Applies shell quoting rules to the given raw-data arguments ๐ Supportsvput(see "Command modifiers") ๐ The first argument picks the operation:[+]e[ncode]ord[ecode]. Encode applies shell quoting to the rest of the line, decode expands it away ๐ If you prefix encode with a plus-sign, the quoted result is not roundtrip enabled ๐ฌ That means it can only be decoded in the very same environment it was encoded in ๐ Also seemle-quote-rndtrip๐ If the coding operation fails, the error number!gets set to^ERR-CANCELED, and your unmodified input becomes the result ๐ The error number might change again because of output or result storage errors ๐ญshellโ [Only new quoting rules] (sh) Pops open an interactive version of the shell and returns its exit status ๐โจshortcut,unshortcutโ [Only new quoting rules] These manage the file- or pathname shortcuts, same as documented forfolder๐unshortcutdeletes every shortcut you give it, or all of them at once if you pass the asterisk*๐งนshortcutshows the full list of shortcuts with no arguments, or the target of one shortcut if you give it one ๐ Otherwise, the arguments come in pairs of a shortcut and its desired expansion, and it creates new ones or updates existing ones ๐ธshiftโ [Only new quoting rules] Shifts the positional parameter stack (starting at 1) by the given number, which has to be a positive decimal, or by 1 if you don't give one ๐ง It's an error if the value goes over the number of positional parameters ๐ซ A0does nothing and counts as a success ๐ซถ You can manage the stack itself withvpospar๐ Heads up: this command will fail in account and hook macros unless the positional parameter stack was explicitly created in the current context viavposparโ ๏ธshowโ Basicallytype, but without MIME decoding or decryption, so you get the raw message text ๐ฆsize(si) โ Shows the size in characters of each message in the given message list ๐sleepโ [Only new quoting rules] Sleeps for the specified number of seconds (and optionally milliseconds) ๐ด It's interruptible by default. If you give a third argument, the sleep becomes uninterruptible ๐ Otherwise, if the sleep gets interrupted, the error number!is set to^ERR-INTR๐ The command fails with^ERR-OVERFLOWif the given duration(s) overflow the time datatype ๐ฑ and with^ERR-INVALif the given durations aren't valid integers ๐ โโ๏ธsort,unsortโunsortis the reset button ๐ It disables sorted or threaded mode, goes back to normal message order, and if theheadervariable is set, shows a header summary โจsortwith no argument shows the current sorting criterion ๐ Give it one, and it creates a sorted representation of the current folder ๐๏ธ It also changes the next command and the addressing modes so they refer to messages in that sorted order ๐ฆ Message numbers stay the same as in regular mode ๐ Ifheaderis set, you get a header summary in the new order too ๐ Want automatic folder sorting? Set theautosortvariable, likeset autosort=thread๐ซถ Here are the possible sorting criterions, iykyk:dateโ Sorts messages by theirDate:field, aka the time they were sent โฐfromโ Sorts messages by the value of theirFrom:field, aka the sender's address ๐ If theshownamevariable is set, the sender's real name gets used instead (if they have one) ๐งsizeโ Sorts messages by their size ๐ฆspamโ [Option] Sorts messages by their spam score, as classified byspamrate๐จstatusโ Sorts messages by their message status ๐ท๏ธsubjectโ Sorts messages by their subject ๐threadโ Creates a threaded display ๐งตtoโ Sorts messages by the value of theirTo:field, aka the recipient's address ๐ฌ Ifshownameis set, the recipient's real name gets used (if any) ๐sourceโ [Only new quoting rules] (so) Reads commands from the given file ๐ "Filename transformations" get applied first ๐ช If the expanded argument ends with a vertical bar|, the argument is treated as a shell command instead, and S-nail reads whatever output that command generates ๐ Depending on the settings ofposixanderrexit, and on whether theignerrcommand modifier was used, running into errors will stop the sourcing of the given input ๐ [v15 behaviour may differ] Heads up:sourcecan't be used from inside macros that run as folder-hooks or accounts ๐ซ In other words, it can only be called from macros that were called ๐source_ifโ [Only new quoting rules] The main difference fromsource(besides not supporting the pipe syntax, aka shell command input) is that this one won't throw an error or even a warning if the given file can't be opened ๐คซ Chill vibes only ๐ฟspamclearโ [Option] Takes a list of messages and clears theiris-spamflag ๐งฝspamforgetโ [Option] Takes a list of messages and makes the spam-interface forget it ever used them to train its Bayesian filter ๐ง ๐จ Unless noted otherwise, the message'sis-spamflag gets inspected to decide whether it's forgotten as "ham" or "spam" ๐ซฃspamhamโ [Option] ๐ Okay bestie, this one takes a list of messages and tells the Bayesian filter of the spam-interface that they're โhamโ (the good, not-spam kind ๐). It also clears theis-spamflag on those messages. โจspamrateโ [Option] ๐ฎ This one takes a list of messages and rates them using the configured spam-interface. The messages themselves stay untouched, but theiris-spamflag gets set as appropriate. Heads up: the spam rating headers get lost, so the rate is forgotten once you leave the mailbox ๐ญ. For the full tea on spam handling in S-nail, check out the manual section โHandling spamโ ๐.spamsetโ [Option] ๐ง Takes a list of messages and sets theiris-spamflag. Simple and iconic ๐ .spamspamโ [Option] ๐ซ Takes a list of messages and tells the Bayesian filter of the spam-interface that they're โspamโ. It also sets theis-spamflag on those messages. Bye bye ๐threadโ [Obsolete] ๐ฅ Same assort thread, so bestie, consider using acommandaliasas necessary.tlsโ [Only new quoting rules] ๐ This is the TLS info and management command multiplexer, helping out with โEncrypted network communicationโ ๐. It's mostly only available if the term,+sockets,is included in features. Commands supportvputif they're documented that way (see โCommand modifiersโ). When something goes wrong, the result shown is always the empty string, so check the error number!for the real drama. For example, string length overflows get caught and set!to^ERR-OVERFLOW. The TLS configuration is honoured, especiallytls-verify๐ก๏ธ.
? vput tls result fingerprint pop3s://ex.am.ple
? echo $?/$!/$^ERRNAME: $result
Its subcommands are:
certchainโ ๐ Shows the complete verified peer certificate chain. Includes informational fields in conjunction withverbose.certificateโ ๐ Shows only the peer certificate, without any signers. Includes informational fields in conjunction withverbose.fingerprintโ ๐ซ Shows thetls-fingerprint-digested fingerprint of the certificate of the givenHOST(server:port, where the port defaults to the HTTPS port, 443).tls-fingerprintis actively ignored for the runtime of this command.Topโ ๐ Liketop, but it always uses the headerpicktypeslot for white- and blacklisting header fields.topโ (to) ๐ธ Takes a message list and types out the firsttoplineslines of each message on your terminal. Unless you've set up a special selection for thetopslot of theheaderpickcommand, the only header fields shown areFrom:,To:,Cc:, andSubject:.topalways uses thetypeheaderpick selection instead. You can squish what's displayed by settingtopsqueeze. Messages get decrypted and converted to the terminal character set if needed.touchโ (tou) ๐ Takes a message list and marks the messages for saving in the โsecondary mailboxโ MBOX. Heads up: S-nail goes its own way from POSIX here, because a followingnextcommand will display the following message instead of the current one. Rebel energy ๐ .Typeโ (T) ๐ฆ Liketype, but it also displays header fields that wouldn't pass the headerpick selection, plus all visualizable parts of MIMEmultipart/alternativemessages.typeโ (t) ๐ Takes a message list and types out each message on your terminal. Which message headers get shown is selectable viaheaderpick. For MIME multipart messages, all parts with a content type oftextshow up, plus all parts with a registered MIME type handler (see โHTML mail and MIME attachmentsโ) that produce plain text output, plus allmessageparts. Everything else is hidden, except for its headers. Messages are decrypted and converted to the terminal character set if necessary. Want the parts that aren't plain-text displayable? Usemimeviewfor those ๐ผ๏ธ.unaccountโ ๐ซฅ Seeaccount.unaliasโ (una) ๐ซฅ Seealias.unansweredโ ๐ซฅ Seeanswered.unbindโ ๐ซฅ Seebind.uncollapseโ ๐ซฅ Seecollapse.uncolourโ ๐ซฅ Seecolour.undefineโ ๐ซฅ Seedefine.undeleteโ ๐ซฅ Seedelete.undraftโ ๐ซฅ Seedraft.unflagโ ๐ซฅ Seeflag.unfwdignoreโ [Obsolete] ๐ฅ Superseded by the multiplexerheaderpick.unfwdretainโ [Obsolete] ๐ฅ Superseded by the multiplexerheaderpick.unignoreโ ๐ฅ Superseded by the multiplexerheaderpick.unmimetypeโ ๐ซฅ Seemimetype.unmlistโ ๐ซฅ Seemlist.unmlsubscribeโ ๐ซฅ Seemlsubscribe.Unreadโ ๐ญ Same asunread.unreadโ ๐ญ Takes a message list and marks each message as not having been read.unretainโ ๐ฅ Superseded by the multiplexerheaderpick.unsaveignoreโ [Obsolete] ๐ฅ Superseded by the multiplexerheaderpick.unsaveretainโ [Obsolete] ๐ฅ Superseded by the multiplexerheaderpick.unsetโ [Only new quoting rules] (uns) ๐ซฅ Seeset.unshortcutโ ๐ซฅ Seeshortcut.unsortโ ๐ซฅ Seeshort.unthreadโ [Obsolete] ๐ฅ Same asunsort.urlcodecโ ๐ Performs URL percent codec operations on the raw-data argument, going by RFC 3986 ๐. The first argument picks the operation:e[ncode]ord[ecode]do plain URL percent en- and decoding, respectively.p[ath]enc[ode]andp[ath]dec[ode]do a slightly modified version that's better for pathnames. It doesn't allow a tilde~, and it won't accept a hyphen-minus-or a dot.as an initial character. The rest of the line is the URL data to convert. This is character set agnostic, so it may decode bytes that are invalid in the currentttycharset. โ ๏ธ [v15 behaviour may differ] This command doesn't know about URLs beyond what's documented here. (vexproffers amakeprintsubcommand, should you want the URL displayed.)
Supports vput (see โCommand modifiersโ), and manages the error number !. If the coding operation fails, the error number ! is set to ^ERR-CANCELED, and the unmodified input is used as the result. The error number may change again due to output or result storage errors.
varshowโ [Only new quoting rules] ๐ Produces the same output as the listing mode ofset, including verbosity adjustments, but only for the given variables.verifyโ [Option] ๐ Takes a message list and verifies each message. If a message isn't an S/MIME signed message, verification will fail for it. The check looks at whether the message was signed using a valid certificate, whether the message sender's email address matches one of those contained within the certificate, and whether the message content has been altered.versionโ ๐ท๏ธ Shows the version and features of S-nail, optionally in a more verbose form that also includes the build and running system environment. Supportsvput(see โCommand modifiersโ).vexprโ [Only new quoting rules] ๐งฎ A multiplexer command offering signed 64-bit numeric calculations, plus other mostly string-based operations. C-style byte string operations are available viacsop. The first argument defines the number, type, and meaning of the remaining arguments. An empty number argument is treated as0. Supportsvput(see โCommand modifiersโ). The result shown in case of errors is-1for usage errors and numeric operations, and the empty string otherwise. โSoftโ errors, like when a search operation failed, will also set the!error number to^ERR-NODATA. Except when otherwise noted, numeric arguments are parsed as signed 64-bit numbers, and errors will be reported in the error number!as the numeric error^ERR-RANGE.
Numeric operations work on one or two signed 64-bit integers. Numbers prefixed with 0x or 0X are read as hexadecimal (base 16), whereas 0 indicates octal (base 8), and 0b as well as 0B denote binary (base 2) numbers. It's possible to use any base in between 2 and 36, inclusive, with the BASE#number notation, where the base is given as an unsigned decimal number, so 16#AFFE is a different way of specifying a hexadecimal number. Unsigned interpretation of a number can be enforced by prefixing a u (case-insensitively), as in u-110. This isn't necessary for power-of-two bases (2, 4, 8, 16 and 32), which will be interpreted as unsigned by default, but it still makes a difference regarding overflow detection and overflow constant. It's possible to enforce signed interpretation by prefixing an s instead (case-insensitively). The number sign notation uses a permissive parse mode, and as such supports complicated conditions out of the box ๐ช:
? wysh set ifs=:;read i;unset ifs;echo $i;vexpr pb 2 10#$i
-009
< -009>
0b1001
One integer is expected by assignment (equals sign =), which does nothing but parse the argument, thus detecting validity and possible overflow conditions. Unary not (tilde ~) creates the bitwise complement, and unary plus and minus are there too. Two integers are used by addition (plus sign +), subtraction (hyphen-minus -), multiplication (asterisk *), division (solidus /) and modulo (percent sign %), as well as for the bitwise operators logical or (vertical bar |, to be quoted), bitwise and (ampersand &, to be quoted), bitwise xor (circumflex ^), the bitwise signed left and right shifts (<<, >>), as well as the unsigned right shift >>>. Math bestie, we did it ๐๐
Okay so pbase is next, and it's lowkey my favourite. ๐ง It takes a number base from 2 to 36 (both ends count, bestie!) and then works on the second number exactly like = does. The difference is the result comes out in whatever base you picked, as a signed 64-bit number, unless you forced unsigned interpretation of the input with a u prefix. ๐ฅ
Numeric operations also have a saturated mode ๐
, unlocked with the ? modifier suffix. The word saturated is optional, so +?, +?satu, and +?saturated are literally the same thing. In saturated mode, overflow errors and division or modulo by zero stop showing up in the exit status. Instead, the result just clings to the minimum or maximum possible value rather than overflowing (or trapping) ๐ซถ This also covers the argument parse step. For the bitwise shifts, the saturated maximum is 63. Any caught overflow gets reported through the error number ! as ^ERR-OVERFLOW.
? vput vexpr res -? +1 -9223372036854775808
? echo $?/$!/$^ERRNAME:$res
0/75/OVERFLOW:-9223372036854775808
The character-set-agnostic string functions don't care about locale settings or character sets at all. ๐
date-utcโ Outputs the current date and time in UTC (Coordinated Universal Time), with values named so thatvput vexpr x date-utc; eval wysh set $xcreates accessible variables. โฐdate-stamp-utcโ Outputs an RFC 3339 internet date/time format of UTC. ๐epochโ The seconds and nanoseconds since the Unix epoch (1970-01-01T00:00:00), namedepoch_secandepoch_nsec, sovput vexpr x epoch; eval wysh set $xcreates accessible variables. ๐ฆfile-expandโ Runs the usual "Filename transformations" on its argument. ๐๏ธfile-stat,file-lstatโ These run the usual "Filename transformations" on the argument, then callstat(2)andlstat(2)respectively, and spit out values so thatvput vexpr x file-stat FILE; eval wysh set $xcreates accessible variables. Thest_typevariable is where it gets fun ๐ It uses:/for directories@for links#for block devices%for character devices|for FIFOs=for sockets.for the rest
randomโ Generates a random string of the given length, or ofPATH_MAXbytes (a constant from/usr/include) if the value0is given. The string isbase64urlencoded according to RFC 4648, so it's a (portable) filename you can actually use. ๐ธ
String operations work, with sufficient support provided, according to the active user's locale encoding and character set (see "Character sets"). Where the ? modifier suffix is supported, there's a case-insensitive mode too ๐ The keyword case is optional, so regex? and regex?case are identical.
makeprint(One-way) โ Converts the argument into something safely printable on the terminal. ๐regex[Option] โ A string operation that tries to match the first argument against the regular expression given as the second argument. The?modifier suffix works here too. If you give an optional third argument, you get a replacement operation instead of the match offset. That third argument is treated as if it were written inside dollar-single-quote (see "Shell-style argument quoting"), and any positional parameter like0,1, etc. gets swapped out for the matching group of the regular expression โจ
? vput vexpr res regex bananarama \
(.*)NanA(.*) '\${1}au\$2'
? echo $?/$!/$^ERRNAME:$res:
1/61/NODATA::
? vput vexpr res regex?case bananarama \
(.*)NanA(.*) '\${1}uauf\$2'
? echo $?/$!/$^ERRNAME:$res:
0/0/NONE:bauauframa:
๐ง vpospar [Only new quoting rules]
Manage the positional parameter stack (see 1, #, *, @ as well as shift). With clear, the positional parameter stack of the current context gets wiped, or the global one if there isn't a current context. ๐งน
With set, the remaining arguments are used to (re)create the stack. If the parameter stack size limit gets exceeded, an ^ERR-OVERFLOW error occurs. ๐จ
With quote, you get a round-trip capable representation of the stack contents. Each quoted parameter is separated from the next by the first character of ifs, and followed by the first character of if-ws, as long as that isn't empty and isn't identical to the first. If that ends up with no separation at all, a space character is used. This mode supports vput (see "Command modifiers"). In other words, the subcommands set and quote can be used together with eval to (re)create an argument stack from and to a single variable, losslessly ๐
? vpospar set hey, "'you ", world!
? echo $#: <${1}><${2}><${3}>
? vput vpospar x quote
? vpospar clear
? echo $#: <${1}><${2}><${3}>
? eval vpospar set ${x}
? echo $#: <${1}><${2}><${3}>
๐ฆ visual (v)
Takes a message list and invokes the VISUAL display editor on each message. Any modified contents get discarded unless the writebackedited variable is set. They're only used if the mailbox can be written to and the editor returns a successful exit status. If you want something less display oriented, edit can be used instead.
๐
write (w)
For conventional messages, the body gets written without all the headers. The original message is never marked for deletion in the originating mail folder. The output is decrypted and converted to its native format as necessary. If the output file already exists, the text gets appended. If a message is in MIME multipart format, its first part is written to the specified file just like a conventional message, and what happens to the remaining parts depends on the execution mode. No special handling of compressed files is performed.
In interactive mode, you're consecutively asked for the filenames of the processed parts. For convenience, you can skip saving any part by giving an empty value, which has the same result as writing it to /dev/null. Shell piping the part content is also supported, by giving a leading vertical bar | character for the filename. Other user input goes through the usual "Filename transformations", including shell pathname wildcard pattern expansions (glob(7)) and shell variable expansion. That's for the message as such, not the individual parts. Also, the contents of the destination file get overwritten if the file previously existed. Character set conversion to ttycharset is performed when saving text data. ๐
๐ [v15 behaviour may differ] In non-interactive mode, any part that doesn't specify a filename is ignored. Suspicious parts of the filenames of the remaining parts are URL percent encoded (as viaurlcodec) to prevent injection of malicious character sequences, which results in a filename that gets written into the current directory. Existing files will not be overwritten. Instead, the part number or a dot is appended after a number sign#to the name until file creation succeeds (or fails due to other reasons).
๐ xcall [Only new quoting rules]
The only difference from call is that the new macro runs in place of the current one, and the current one won't regain control. All resources of the current macro get released first. This means any setting covered by localopts gets forgotten, and covered variables become cleaned up. If this command isn't used from within a called macro, it silently becomes a (more expensive variant of) call. ๐งผ
๐ xit (x)
A synonym for exit. ๐
๐ซถ z [Only new quoting rules]
S-nail presents message headers in screenfuls, just like the headers command does. Without arguments, this command scrolls to the next window of messages, and so does an argument of +. An argument of - scrolls to the last one, ^ scrolls to the first, and $ scrolls to the last screen of messages. A number argument prefixed with + or - is calculated relative to the current position, while a number without a prefix specifies an absolute position. ๐
๐ Z [Only new quoting rules]
Similar to z, but this one scrolls to the next or previous window that contains at least one 'new' or flagged message. Main character energy ๐
โจ COMMAND ESCAPES โจ
Command escapes are available in "Compose mode" during interactive usage, when explicitly requested via -~, and in batch mode (-#). They do special things like editing the headers of the message being composed, calling normal "COMMANDS", yielding a shell, and so on ๐ธ Command escapes are only recognized at the beginning of lines, and they consist of an escape followed by a command character. The default escape character is the tilde ~. Iykyk, and now you do! ๐ฆ
Okay so, unless a command escape says otherwise, escapes keep the error number ! and the exit status ? up to date ๐
The variable errexit decides whether a failed operation errors out, message compose mode, and causes the program to exit ๐ซถ You can prefix an escape with nothing, or with several single-character command modifiers, and any whitespace in between gets ignored โจ
-โ Hyphen-minus gives you the same effect as the command modifierignerr, and it overrideserrexit๐ฅ$โ Dollar evaluates the rest of the line. Check out โShell-style argument quotingโ for more on that ๐ง [v15 behaviour may differ] For now the entire input line gets evaluated as a whole, so control operators like semicolon;can get interpreted when you didn't mean them to. Quote them to avoid that ๐
Adding the command line to the [Option]al history can be prevented by putting whitespace directly after the escape ๐ The [Option]al key bindings support a compose mode specific context. These are the command escapes that are supported:
~~ stringโ Inserts the string into the message, prefaced by a single~๐ If the escape character has been changed, that character has to be doubled instead.~! commandโ Runs the shell command that follows ๐ Unescaped exclamation marks get replaced with the previously executed command if the internal variablebangis set, and then you're back in the message.~.โ Ends compose mode and sends the message ๐ Here's the order of things, bestie:- The hooks
on-compose-splice-shellandon-compose-spliceget called first, in that order, when they're set. - In interactive mode,
askatend(which leads toaskccandaskbcc) gets checked, andaskattachgets checked as well, plusasksend. - A set
on-compose-leavehook gets called next. autoccandautobccget joined in if they're set.- A given
message-inject-tailgets incorporated. - Then compose mode is left, and that's the end of it ๐ฆ
- The hooks
~: S-nail-commandor~_ S-nail-commandโ Lets you run โCOMMANDSโ from inside compose mode, since those are allowed here too ๐~< filenameโ Identical to~r๐ธ~<! commandโ The command gets executed using the shell, and its standard output is inserted into the message ๐~?โ [Option] Writes a summary of the command escapes ๐~@ [filename...]โ Appends to or edits the list of attachments ๐ It does not manage the error number!and the exit status?, so use~^if you need proper error handling ๐- In append mode, you give a list of filename arguments as shell tokens (see โShell-style argument quotingโ, and token-separating commas are ignored too). They get interpreted the same way as documented for the command line option
-a, with the message number exception below. - Without filename arguments, the attachment list gets edited entry by entry. If a filename is left empty, that attachment is deleted from the list. Once you hit the end of the list, you can either enter new attachments or quit by committing an empty โnewโ attachment โจ
- In non-interactive mode or in batch mode (
-#), the list of attachments isn't really edited. It gets recreated instead, and again an empty input ends list creation. - In every mode, if a filename consists solely of the number sign
#followed by either a valid message number of the currently active mailbox, or a period.(referring to the current message of the active mailbox, aka the so-called โdotโ), then that message gets attached as amessage/rfc822MIME message part ๐ฆ The number sign must be quoted so it doesn't get mistaken for a shell comment character ๐
- In append mode, you give a list of filename arguments as shell tokens (see โShell-style argument quotingโ, and token-separating commas are ignored too). They get interpreted the same way as documented for the command line option
~| commandโ Pipes the message text through the filter command you specify ๐ If the command gives no output or terminates abnormally, the original text of the message is kept. The commandfmt(1)is often used as a rejustifying filter ๐- If the first character of the command is a vertical bar
|, then the entire message, including the header fields, is subject to the filter command. So~|| echo Fcc: /tmp/test; catwill prepend a file-carbon-copy message header ๐ญ Also see~eand~v.
- If the first character of the command is a vertical bar
~^ cmd [subcmd [arg3 [arg4]]]โ Inspects and modifies the message using the semantics ofdigmsg, so the arguments get evaluated according to โShell-style argument quotingโ ๐ Error number!and exit status?aren't managed here. Errors get handled via the protocol instead, and hard errors like I/O failures can't be handled at all ๐
Heads up: the protocol is made up of command lines, each followed by response line(s). The first field of a response line is a status code. It tells you whether a command was successful, whether result data is to be expected, and if so, what format that data is in. Response data gets shell quoted as necessary so it can be consumed byreadsh, or byevalandvpospar, to name a few. Error status code lines may optionally contain additional context.
The status codes you can get back are:
210โ Status ok. The remains of the line are the result โจ211โ Status ok. The rest of the line is optionally used for more status. What follows are lines of result addresses, terminated by an empty line ๐ฌ All the input, including that empty line, must be consumed before further commands can be issued. Address lines consist of two tokens. The first is the plain network address, e.g.,bob@exam.ple, followed by the (quoted) full address as known:'(Lovely) Bob <bob@exam.ple>'๐ Non-network addresses use the first field to indicate their type instead (hyphen-minus-for files, vertical bar|for pipes, and number sign#for names which will undergo alias processing), and the actual value goes in the second field.212โ Status ok. The rest of the line is optionally used for more status. What follows are lines of furtherly unspecified (quoted) string content, terminated by an empty line. All the input, including the empty line, must be consumed before further commands can be issued ๐งพ500โ Syntax error; invalid command ๐ซ501โ Syntax error, or otherwise invalid parameters or arguments ๐505โ Error: an argument fails verification. For example, an invalid address has been specified (also seeexpandaddr), or someone tried to modify anything in S-nail's own namespace, or a modifying subcommand got used on a read-only message ๐506โ Error: an otherwise valid argument gets rendered invalid due to context. For example, a second address gets added to a header which may consist of a single address only ๐ค
If a command indicates failure, the message will have stayed unmodified ๐ก๏ธ Most commands can fail with 500 if required arguments are missing, or if excessive arguments were given (false command usage). [v15 behaviour may differ] The latter doesn't happen regularly yet, because, as stated in โShell-style argument quotingโ, our argument parser isn't smart enough to work on a subcommand basis ๐ค For example, you might get an excess argument error for a three-argument subcommand that receives four arguments, but not for a four-argument subcommand that receives six arguments. Here the excess gets joined. The following (case-insensitive) commands are supported:
attachmentโ Lets you list, remove, and add message attachments ๐ The second argument specifies the subcommand to apply, and it's one of these:attributeโ Uses the same search mechanism described forremove, and prints any known attributes of the first found attachment via212upon success, or501if no such attachment can be found. The attributes are written as lines with a keyword and a value token ๐ท๏ธattribute-atโ Uses the same search mechanism described forremove-at, and is otherwise identical toattribute๐ฏattribute-setโ Uses the same search mechanism described forremove, and sets the attribute given as the fourth token to the value given as the fifth token argument โ๏ธ If the value is an empty token, the given attribute is removed, or reset to a default value if the attribute's existence is crucial.- It returns via
210upon success, with the index of the found attachment following. It returns505for message attachments or if the given keyword is invalid, and501if no such attachment can be found ๐ - The following keywords may be used (case-insensitively):
- It returns via
filename๐: sets the filename of the MIME part, which is the name that shows up for display and gets suggested when someone saves it ๐พcontent-descriptionโจ: attaches some descriptive info to the attachment's content. Some MUAs prefer this over the plain filename, so it's lowkey a glow-up ๐content-id๐: can be used to uniquely identify MIME entities in several contexts. It expects a special reference address format defined in RFC 2045, and if the address content fails verification it generates505๐content-type๐: defines the media type/subtype of the part. It's managed automatically, but you're allowed to overwrite it ๐content-disposition๐ฆ: automatically set to the stringattachment
attribute-set-at ๐
This uses the same search mechanism described for remove-at, and is otherwise identical to attribute-set ๐.
attachment: insert ๐ฅ
Adds the attachment given as the third argument. It's specified exactly like the command line option -a, and it supports the message number extension documented for ~@ ๐ง. It reports 210 on success, followed by the index of the new attachment. It reports 505 if the file can't be opened, and 506 if an on-the-fly character set conversion fails. Otherwise it reports 501, which also happens if character set conversion is requested but isn't available ๐ญ.
attachment: list ๐
Lists all attachments via 212, or reports 501 if there are no attachments at all. This is the default command of attachment if no second argument is given ๐ซถ.
attachment: remove ๐๏ธ
Removes the attachment given as the third argument, and reports 210 on success or 501 if no such attachment can be found ๐. If there's any path component in the argument, an exact match of the path used to create the attachment is used directly. But if only the basename matches, all attachments get traversed to find an exact match first, and then the removal happens ๐ฅ. If multiple basenames match, you get a 506 error. Message attachments are treated as absolute pathnames ๐.
If there's no path component in the argument, then all attachments are searched for filename= parameter matches, as well as matches of the basename of the path that was used when the attachment was created. Multiple matches result in a 506 ๐
โโ๏ธ.
attachment: remove-at ๐ฏ
This interprets the third argument as a number and removes the attachment at that list position (counting from one!) ๐ข. It reports 210 on success, 505 if the argument isn't a number, or 501 if no such attachment exists ๐
.
header ๐ธ
This command lets you list, inspect, and edit message headers ๐. Header name case is not normalized, so you should use case-insensitive comparison when matching names ๐. The second argument picks the subcommand, and it's one of the following:
header: insert โ๏ธ
Creates a new instance (or an additional one) of the header given in the third argument, with the header body content given in the fourth token ๐. It may return 501 if the third argument is a free-form header field name that's invalid, or if extracting the body content fails. It returns 505 if any extracted address fails syntax and/or security checks, or on S-nail namespace violations. It returns 506 to prevent you from exceeding a single-instance header ๐ซ. Heads up: Subject: can be appended to, and a space separator gets added automatically first โจ.
To:, Cc: and Bcc: support the ?single modifier, which enforces treatment as a single addressee. For example: header insert To?single: 'exa, <m@ple>'. The word single is optional ๐ง.
210 is returned on success, followed by the name of the header and the list position of the newly inserted instance. The list position is always 1 for single-instance header fields. All free-form header fields are managed in a single list, so also see customhdr ๐.
header: list ๐
Without a third argument, you get a list of all existing headers via 210. This is the default command of header if no second argument is given. A third argument restricts the output to just the given header, and that may fail with 501 if no such field is defined ๐ซถ.
header: remove ๐๏ธ
Removes all instances of the header given as the third argument. It reports 210 on success, 501 if no such header can be found, and 505 on S-nail namespace violations ๐.
header: remove-at ๐ฏ
Removes, from the header given as the third argument, the instance at the list position (counting from one!) given with the fourth argument ๐ข. It reports 210 on success. It reports 505 if the list position argument isn't a number or on S-nail namespace violations, and 501 if no such header instance exists ๐
.
header: show ๐
Shows the content of the header given as the third argument. Depending on the header type, this may respond with 211 or 212. Any failure results in 501 ๐ญ.
In compose-mode, you get read-only access to optional pseudo headers in the S-nail private namespace ๐ฎ:
Mailx-Command:๐งพ: the name of the command that generates the message. It's one offorward,Lreply,mail,Reply,reply, orresend. This pseudo header always exists in compose-mode ๐.Mailx-Raw-To:,Mailx-Raw-Cc:,Mailx-Raw-Bcc:๐ง: these represent the frozen initial state of these headers, before any transformation (alias, alternates, recipients-in-cc etc.) took place ๐ฆ.Mailx-Orig-Sender:,Mailx-Orig-From:,Mailx-Orig-To:,Mailx-Orig-Cc:,Mailx-Orig-Bcc:๐จ: the values of those headers from the original message that got addressed by any of reply, forward, or resend. The sender field is special: it's filled in with the sole sender according to RFC 5322 rules, so it may be equal to the from field ๐ค.
help, ? ๐
Shows an abstract of the above commands via 211 ๐.
version ๐
Prints the protocol version via 210 โจ.
~A ๐๏ธ
The same as ~i Sign ๐
.
~a ๐๏ธ
The same as ~i sign ๐
.
~b name ... ๐
Adds the given names to the list of blind carbon copy recipients ๐ซฃ.
~c name ... ๐ฎ
Adds the given names to the list of carbon copy recipients ๐.
~d ๐
Reads the file specified by the DEAD variable into the message ๐ง.
~eโ Okay bestie, this one pops open your EDITOR on whatever you've written so far, then drops you right back into compose mode ๐~vis the fancier, more display-oriented option, and~||is the pipe-based vibe ๐ฆ~F messagesโ This grabs the named messages and drops them into your draft, headers and MIME parts included โจ It honours forward-add-cc, forward-inject-head and forward-inject-tail. No messages named? It grabs the current message, the โdotโ ๐~f messagesโ Same idea, but way more curated ๐ It trims the header fields using the โforwardโ white- and blacklist selection of headerpick (or โtypeโ when posix is on). It also honours forward-add-cc, forward-inject-head and forward-inject-tail. For MIME multipart messages, only the first displayable part comes along ๐ง No messages named? Dot again.~Hโ In interactive mode, you get to editFrom:,Reply-To:andSender:one at a time ๐ธ The starting values come from the from, reply-to and sender variables. In non-interactive mode, this sets ^ERR-NOTTY, so, not it ๐~hโ Interactive mode only: editTo:,Cc:,Bcc:andSubject:one by one ๐ Non-interactive mode sets ^ERR-NOTTY, same as before ๐~I variableโ Pops the value of the variable you name right into your message ๐ If the variable is unset or empty, your message stays exactly how it was. In posix mode, the sequences\t(horizontal tab) and\n(line feed) get expanded. Otherwise the expansion happens at set time ([v15 behaviour may differ] by using the command modifier wysh) ๐ซถ~i variableโ Literally the same as~I, except it tacks a newline on the end โจ~M messagesโ Reads the named messages in, indented by indentprefix ๐ No messages? Current message, the โdotโ. It honours forward-add-cc, forward-inject-head and forward-inject-tail ๐ฆ~m messagesโ Same indented energy as~M, but this one strips the header fields using the โtypeโ white- and blacklist selection of headerpick ๐ธ It also honours forward-add-cc, forward-inject-head and forward-inject-tail. For MIME multipart messages, only the first displayable part is included ๐ฅ No messages named? Dot.~pโ Shows you the message collected so far, with the header fields at the top and the attachment list at the bottom if there are any ๐~Qโ Reads in the given (or current) message(s) using the quote algorithm ๐ Quote is implicitly assumed here even if it isn't set. It honours quote-add-cc ๐~qโ Aborts the message you're writing ๐ If save is set, it copies the message to the file the DEAD variable points at ๐ญ~R filenameโ Identical to~r, but every line that gets read in is indented by indentprefix ๐ง~r filename [HERE-delimiter]โ Reads the named file into your message ๐ซถ This is subject to โFilename transformationsโ, but shell globs and variable expansions are excluded. If filename is the hyphen-minus-, standard input gets used instead, which is great for pasting โจ HERE-delimiter can only be given in this stdin mode. If you use one, it'll keep reading until that delimiter shows up alone on a line. Hitting EOF before that is an error ๐ The HERE-delimiter is a required argument in non-interactive mode. If it's single-quote quoted, the pasted content won't be expanded. [v15 behaviour may differ] otherwise, a future version of S-nail may perform shell-style expansion on the content ๐~s stringโ Makes the named string your current subject field ๐ Heads up: newline (NL) and carriage-return (CR) bytes are invalid, and they'll be normalized to space (SP) characters โ ๏ธ~t name ...โ Adds the given name(s) to your direct recipient list ๐~U messagesโ Reads in the given (or current) message(s) without any headers, indented by indentprefix ๐ It honours forward-add-cc, forward-inject-head and forward-inject-tail ๐~u messagesโ Same as~U, but without the indentation ๐ Reads in the given (or current) message(s), excluding all headers. It honours forward-add-cc, forward-inject-head and forward-inject-tail ๐ซถ~vโ Invokes the VISUAL editor on your message so far, then returns you to compose mode ๐ฆ~eis the less display-oriented choice, and~||is the pipe-based approach ๐ธ~w filenameโ Writes the message onto the named file, subject to the usual โFilename transformationsโ ๐ If the file already exists, the message gets appended to it ๐ง~xโ Same as~q, except the message isn't saved at all, not even a little ๐
โจ INTERNAL VARIABLES โจ
Internal S-nail variables are controlled with the set and unset commands ๐ Prefix a variable name with โnoโ and call set, and it does the same thing as unset. So unset crt and set nocrt are identical ๐
varshow gives you more insight on the variable(s) you name. If you call set with no arguments, it shows a listing of all variables. Both commands support a more verbose listing mode ๐ฅ Some well-known variables get inherited from the program โENVIRONMENTโ implicitly. Others can be imported explicitly with the command environ, and then they share those same properties โจ
There are two kinds of internal variables, and both can form chains ๐ Boolean variables can only be in one of two states, โsetโ or โunsetโ. Value variables hold an (optional) string value. For value variables, proper quoting is necessary at assignment time. The introduction of the section โCOMMANDSโ documents the supported quoting rules ๐ซถ
Here's a quick example of assignment and quoting, bestie ๐ง
? wysh set one=val\ 1 two="val 2" \
three='val "3"' four=$'val \'4\''; \
varshow one two three four; \
unset one two three four
Depending on the actual option string, its value might get interpreted as colour names, command specifications, normal text, and more ๐ Values can be treated as numbers. If a value is documented as decimal, decimal is what's expected. Otherwise, any numeric format and base that the vexpr command understands is fair game too ๐ข
There's also a special kind of string value called the โboolean stringโ ๐ It must either be a decimal integer (where โ0โ is false, and โ1โ and any other value are true), or one of these case-insensitive strings. โoffโ, โnoโ, โnโ and โfalseโ mean false. โonโ, โyesโ, โyโ and โtrueโ mean true ๐ A special kind of boolean string is the โquadoptionโ. It can optionally be prefixed with the case-insensitive term โask-โ, as in โask-yesโ. In interactive mode the user gets prompted. Otherwise the actual boolean is used ๐
Variable chains extend a plain โvariableโ with โvariable-HOSTโ and โvariable-USER@HOSTโ variants ๐ฆ โHOSTโ gets converted to all lowercase when it's looked up (but not when the variable is set or unset!). It may also be [Option]ally IDNA converted. It means โserver:portโ if a โportโ was specified in the contextual Uniform Resource Locator URL. See โOn URL syntax and credential lookupโ ๐ธ Even though this mechanism is based on URLs, no URL percent encoding may be applied to either โUSERโ or โHOSTโ. Variable chains need raw data, and the mentioned section has examples ๐ Variables that support chains are explicitly documented as such. S-nail treats the base name of any such variable as special, so please don't create custom names like โvariable-xyzโ to avoid false classifications and treatment of those variables ๐
๐ Initial settings ๐
The standard POSIX 2008/Cor 2-2016 mandates the following initial variable settings: noallnet, noappend, asksub, noaskbcc, noautoprint, nobang, nocmd, nocrt, nodebug, nodot, escape set to โ~โ, noflipr, nofolder, header, nohold, noignore, noignoreeof, nokeep, nokeepsave, nometoo, nooutfolder, nopage, prompt set to โ? โ, noquiet, norecord, save, nosendwait, noshowto, noSign, nosign, toplines set to โ5โ ๐
However, S-nail has built in some initial (and some default) settings which (may) diverge from that list ๐ Others can be adjusted by one of the โResource filesโ. To display the former, use set: $ s-nail -:/ -v -Xset -Xx ๐
In general, this implementation sets (and has extended the meaning of) sendwait, and it does not support the noonehop variable. Use command line options or mta-arguments to pass options through to an mta โจ The default global resource file sets, among others, the variables hold, keep and keepsave, and establishes a default headerpick selection etc. So you should take those into account ๐ซถ
๐ Variables ๐
?โ (Read-only) This is the exit status of the last command, or the return value of the macro called last ๐ง It has a meaning in the state machine. In conjunction with errexit, any non-0 exit status causes a program exit. In posix mode, any error while loading (any of the) resource files has the same effect ๐ฅ ignerr, one of the โCommand modifiersโ, can be used to tell the state machine to ignore errors ๐!โ (Read-only) This is the current error number (errno(3)), which gets set after an error happens ๐ It's also available via ^ERR, and you can query the error name and documentation string with ^ERRNAME and ^ERRDOC. [v15 behaviour may differ] This machinery is new, so the error number is only really usable if a command explicitly states that it manages the variable !. For others, errno gets used in case of errors, or ^ERR-INVAL if that is 0. So it may or may not reflect the real error ๐ญ The error number may be set with the command return ๐^: ๐ฎ (Read-only) Okay, this is a multiplexer variable, and it's giving dynamic. It expands into whatever state or condition you ask for. The options are:^ERR,^ERRDOC,^ERRNAME: The number, documentation, and name of the currenterrno(3), respectively, which is usually set after an error occurred. The documentation is an [Option], and the name gets used if no documentation is available. [v15 behaviour may differ] This machinery is brand new and is usually reliable only if a command explicitly says it manages the variable!, which is effectively identical to^ERR. Each of those can be suffixed with a hyphen-minus followed by a name or number, and then the expansion refers to that given error. It's a direct mapping of (a subset of) the system error values, so here's a little demo ๐ :
define work {
eval echo \$1: \$^ERR-$1:\
\$^ERRNAME-$1: \$^ERRDOC-$1
vput vexpr i + "$1" 1
if [ $i -lt 16 ]
\xcall work $i
end
}
call work 0
^ERRQUEUE-COUNT, ^ERRQUEUE-EXISTS: ๐ง The number of messages in the [Option]al queue of errors, plus a string showing the queue state: either empty or (translated) โERRORโ. These are always 0 and the empty string, respectively, unless features includes ,+errors,.
*: ๐ (Read-only) Expands all positional parameters (see1), separated by the first character of the value ofifs. [v15 behaviour may differ] The special semantics of the equally named special parameter of the sh(1) are not supported yet, so don't count on those.@: ๐ธ (Read-only) Expands all positional parameters (see1), separated by a space character. If you put it in double quotation marks, each positional parameter gets properly quoted so it expands back to a single parameter again. Iconic behaviour, honestly.#: ๐ข (Read-only) Expands to the number of positional parameters, which is the size of the positional parameter stack in decimal.0: ๐ฆ (Read-only) Inside the scope of a defined and called macro, this expands to the name of the calling macro, or to the empty string if the macro is running from top-level. For the [Option]al regular expression search and replace operator ofvexpr, this expands to the entire matching expression. It represents the program name in global context.1: ๐ (Read-only) This is access to the positional parameter stack. All further parameters can be reached with this syntax too:2,3, and so on. Positional parameters can be shifted off the stack by callingshift. The stack holds, for example, the arguments of a called defined macro and the matching groups of the [Option]al regular expression search and replace expression ofvexpr, and it can be explicitly created or overwritten with the commandvpospar.account: ๐ผ (Read-only) Is set to the active account.add-file-recipients: ๐ (Boolean) When file or pipe recipients have been specified, this mentions them in the corresponding address fields of the message instead of quietly stripping them from the recipient list. By default such addressees are not mentioned.allnet: ๐ (Boolean) Causes only the local part to be evaluated when comparing addresses.append: โ (Boolean) Causes messages saved in the โsecondary mailboxโ MBOX to be appended to the end rather than prepended. This should always be set, bestie.askatend: โณ (Boolean) Causes the prompts for โCc:โ and โBcc:โ lists to show up after the message has been edited.askattach: ๐ (Boolean) If set, S-nail asks an interactive user for files to attach at the end of each message. An empty line finalizes the list.askcc: ๐ (Boolean) Causes the interactive user to be prompted for carbon copy recipients, at the end of each message ifaskatendorbsdcompatare set.askbcc: ๐ (Boolean) Causes the interactive user to be prompted for blind carbon copy recipients, at the end of each message ifaskatendorbsdcompatare set.asksend: ๐ (Boolean) Causes the interactive user to be prompted for confirmation to send the message, or to reenter compose mode, after being shown a preliminary envelope summary.asksign: โ๏ธ (Boolean)[Option] Causes the interactive user to be prompted about whether the message is to be signed at the end of each message. Thesmime-signvariable gets ignored when this one is set.asksub: ๐ท๏ธ (Boolean) Causes S-nail to prompt the interactive user for the subject upon entering compose mode, unless a subject already exists.attrlist: ๐ A sequence of characters to display in the โattributeโ column of the headline, as shown in the display of headers. Each character stands for one type of message (see โMessage statesโ). The default is โNUROSPMFAT+-$~โ, or โNU *HMFAT+-$~โ if thebsdflagsvariable is set. The order goes like this:- โNโ: new.
- โUโ: unread but old.
- โRโ: new but read.
- โOโ: read and old.
- โSโ: saved.
- โPโ: preserved.
- โMโ: mboxed.
- โFโ: flagged.
- โAโ: answered.
- โTโ: draft.
- โ+โ: [v15 behaviour may differ] start of a (collapsed) thread in threaded mode (see
autosort,thread); - โ-โ: [v15 behaviour may differ] an uncollapsed thread in threaded mode; only used in conjunction with
-L. - โ$โ: classified as spam.
- โ~โ: classified as possible spam.
autobcc: ๐ซฃ Specifies a list of recipients who automatically get a blind carbon copy of each outgoing message.autocc: ๐ Specifies a list of recipients who automatically get a carbon copy of each outgoing message.autocollapse: ๐งต (Boolean) Causes threads to be collapsed automatically when thethreaded sort mode (โthreadedโ) is entered. See thecollapsecommand.autoprint: ๐ (Boolean) Enables automatic typing of a(n existing) โsuccessiveโ message afterdeleteandundeletecommands. The message that becomes the new โdotโ is shown automatically, as viadpordt.autosort: ๐๏ธ Causes sorted mode (see thesortcommand) to be entered automatically when a folder is opened, using the value of this variable as the sorting method. For example,set autosort=thread.bang: โ (Boolean) Enables the substitution of all not (reverse-solidus) escaped exclamation mark โ!โ characters with the contents of the last executed command, for the!shell escape command and~!, one of the compose mode โCOMMAND ESCAPESโ. If this variable is not set, no reverse solidus stripping is performed.bind-timeout: ๐ชฆ [Obsolete] This is the predecessor ofbind-inter-byte-timeout. [v15 behaviour may differ] Setting this automatically sets the successor.bind-inter-byte-timeout: โฑ๏ธ [Option] Terminals may generate multi-byte sequences for special function keys, for example, but those sequences may not get read as a unit. Multi-byte sequences can also be defined freely viabind. This variable sets the timeout in milliseconds that the MLE (see โOn terminal control and line editorโ) waits for more bytes to arrive unless it considers a sequence โcompleteโ. The default is200, and the maximum is about 10 seconds. In the following example, the comments state which sequences are affected by this timeout:
? bind base abc echo 0 # abc
? bind base ab,c echo 1 # ab
? bind base abc,d echo 2 # abc
? bind base ac,d echo 3 # ac
? bind base a,b,c echo 4
? bind base a,b,c,d echo 5
? bind base a,b,cc,dd echo 6 # cc and dd
bind-inter-key-timeout: ๐ฐ๏ธ [Option] Multi-key bind sequences do not time out by default. If this variable is set, the current key sequence is forcefully terminated once the timeout (in milliseconds) triggers. The value should be (maybe significantly) larger thanbind-inter-byte-timeout, but it may not exceed the maximum, too.bsdcompat: ๐ฉ (Boolean) Sets some cosmetic features to traditional BSD style. It has the same effect as settingaskatendand all other variables prefixed with โbsdโ. It also changes the behaviour ofemptystart, which does not exist in BSD.bsdflags: ๐ (Boolean) Changes the letters shown in the first column of a header summary to traditional BSD style.bsdheadline: ๐ฐ (Boolean) Changes the display of columns in a header summary to traditional BSD style.bsdmsgs: ๐ฌ (Boolean) Changes some informational messages to traditional BSD style.bsdorder: ๐ (Boolean) Causes the โSubject:โ field to appear immediately after the โTo:โ field in message headers, and with the~hโCOMMAND ESCAPESโ.build-cc,build-ld,build-os,build-rest: ๐ ๏ธ (Read-only) This is the build environment: the compiler, the linker, and the operating system S-nail has been built for. The OS is usually taken from uname(1) viauname -sand then lowercased.build-restholds all the possibly interesting rest of the configuration and build environment. This info is also available in the verbose output of the commandversion. Literal main character energy ๐โจ
charset-7bit โจ
okay so this is the value that shows up in the charset= part of Content-Type: MIME headers when S-nail did not convert the message data to another character set ๐
it defaults to US-ASCII, and whatever you pick has to be US-ASCII compatible, no cap ๐ธ
charset-8bit ๐ฆ
[Option] this is the default 8-bit character set that gets tacked on as the implicit last member of the variable sendcharsets ๐ it defaults to UTF-8 if character set conversion is available, and to ISO-8859-1 otherwise ๐ญ the exception is when the OS is known to always and exclusively support UTF-8 locales. then the only supported character set is ttycharset and this variable is effectively ignored ๐
charset-unknown-8bit ๐
[Option] okay, so RFC 1428 says when internet mail gateways should "upgrade" a message by using a character set named unknown-8bit ๐ง since that charset is super unclassified, S-nail can't convert it to any other charset, period ๐ค if this variable is set, any message part using unknown-8bit is assumed to really be in whatever charset the value says. otherwise, the (final) value of charset-8bit gets used instead โจ
bestie, this one also kicks in when a MIME type (see "The mime.types files") of a message part using the binary character set gets forcefully treated as text ๐
cmd ๐
the default value for the pipe command ๐ซถ
colour-disable ๐
(Boolean) [Option] forcefully disables the usage of colours ๐ค also check out the section "Coloured display" ๐
colour-pager ๐ธ
(Boolean) [Option] whether colour gets used for output that's paged through PAGER ๐ heads up: pagers may need special command line options. less(1) needs -R and lv(1) needs -c to show colours ๐ญ honestly you usually don't have to fiddle, since S-nail may make adjustments on its own depending on the value of the environment variable PAGER (see there for more) โจ
contact-mail, contact-web ๐
(Read-only) the contact addresses for email and web, respectively, for bug reports, suggestions, or anything else about S-nail ๐ซถ the email one can be used directly, like ? eval mail $contact-mail ๐
content-description-forwarded-message, content-description-quote-attachment, content-description-smime-message, content-description-smime-signature ๐
[Option] (partially) these are strings that get placed in the matching Content-Description: headers if they're non-empty ๐ฆ they all come with default values, like Forwarded message ๐ท
crt ๐ฅ
in an (interactive) terminal session, if this variable is set, it becomes the threshold for how many lines the output has to span before it gets shown through the configured PAGER ๐ set it to 0 to force the pager. set it without a value and S-nail will figure out the current height of your terminal screen to work out the threshold (see LINES, screen and stty(1)) ๐ [v15 behaviour may differ]
rn it counts the lines of the message in wire format, which, depending on the mime-encoding, can be totally unrelated to the number of display lines ๐ (the software is old, and historically that relation was just a given, bestie ๐)
customhdr ๐ง
this lets you define a set of custom headers to inject into newly composed or forwarded messages โจ a custom header is the field name, then a colon :, then the field body. standard header field names can't be overwritten by a custom header, except Comments: and Keywords: ๐
different from the command line option -C, the variable value is read as a comma-separated list. to put commas inside header bodies, escape them with a reverse solidus \ ๐ you can manage headers more freely in "Compose mode" via ~^ ๐
? set customhdr='Hdr1: Body1-1\, Body1-2, Hdr2: Body2'
datefield ๐๏ธ
this controls the look of the %d date and time format spec of the headline variable, the one used when you view the header summary, for example ๐ if unset, the local receiving date gets used and displayed unformatted. otherwise it uses the message's sending Date: ๐ฌ you can assign a strftime(3) format string to control formatting, but embedding newlines via the %n format isn't supported and will cause display errors ๐ญ the default is %Y-%m-%d %H:%M, and also see datefield-markout-older ๐
datefield-markout-older ๐ฆ
only used together with datefield ๐ซถ this creates a visible distinction for messages dated more than a day in the future, or older than six months. it's kinda like the -l option of the POSIX utility ls(1) ๐ if set to the empty string, you get the plain month, day and year of the Date:, but you can still assign a strftime(3) format string to control formatting ๐
the default is %Y-%m-%d โจ
debug ๐งช
(Boolean) (Almost) this enters a debug-only sandbox mode that generates lots of log messages, disables the actual delivery of messages, and also implies norecord as well as nosave ๐ฑ also see verbose ๐
disposition-notification-send ๐จ
(Boolean) [Option] emits a Disposition-Notification-To: header (RFC 3798) with the message ๐งพ this requires the from variable to be set ๐ฅ
dot ๐ธ
(Boolean) when dot is set, a period . on a line by itself during message input in "Compose mode" (interactive or batch -#) counts as end-of-message, on top of the normal end-of-file condition ๐ซถ this behaviour is implied in posix mode with a set ignoreeof ๐
dotlock-disable ๐
(Boolean) [Option] disables creation of "dotlock files" for MBOX databases ๐
dotlock-ignore-error โ ๏ธ
[Obsolete] (Boolean) [Option] ignores failures when creating "dotlock files" ๐ please use dotlock-disable instead ๐ซถ
editalong โ๏ธ
if this variable is set, the editor starts automatically when a message gets composed in interactive mode ๐ if the value starts with the letter v, it acts as if ~v had been specified. otherwise it acts as if ~e had been specified (see "COMMAND ESCAPES") ๐
the editheaders variable is implied for this automatically spawned editor session ๐ฆ
editheaders ๐
(Boolean) when a message gets edited while being composed, its header is included in the editable text ๐ท
emptystart ๐
(Boolean) when entering interactive mode, S-nail normally writes "No mail for user" and exits immediately if a mailbox is empty or doesn't exist ๐ญ if this variable is set, S-nail starts even with an empty or non-existent mailbox (the latter behaviour furtherly depends upon bsdcompat, though) ๐ง
errexit ๐จ
(Boolean) every command with a non-0 exit status, including every called macro that returns a non-0 status, causes a program exit unless it's prefixed by ignerr (see "Command modifiers") ๐ this also affects "COMMAND ESCAPES", but those use a different modifier for ignoring the error ๐ for more on this topic, refer to the variable ? ๐
errors-limit ๐
[Option] the maximum number of entries in the errors queue ๐ซถ
escape ๐
the first character of this value defines the escape character for "COMMAND ESCAPES" in "Compose mode" ๐
the default is the tilde ~ ๐ if set to the empty string, command escapes are disabled ๐
expandaddr ๐
okay this one's a whole vibe ๐ if unset, only user name and email address recipients are allowed "On sending mail, and non-interactive mode" ๐ญ if set without a value, all possible recipient types will be accepted ๐ซถ
a value is parsed as a comma-separated list of case-insensitive strings. if that list contains restrict, the behaviour acts like the unset one, except when you're in interactive mode, or when "COMMAND ESCAPES" were enabled via -~ or -#. then it acts like the set-without-value one and allows all address types ๐ restrict really acts like restrict,-all,+name,+addr, so be careful with ordering issues ๐ตโ๐ซ
recipient types can be added with a plus sign + prefix, or removed with a hyphen-minus - prefix ๐ฏ by default, invalid or disallowed types get filtered out and cause a warning. hard send errors need to be enforced by including fail ๐จ here's what the values mean:
allโ covers all types ๐fccโ whitelistsFcc:header targets regardless of other settings ๐fileโ file targets (it includesfcc) ๐pipeโ command pipeline targets ๐งnameโ user names still unexpanded after alias and mta-aliases processing, and thus left for expansion by the mta (invalid for the built-in SMTP one) ๐งธaddrโ network addresses ๐
targets are interpreted in the given order, so restrict,fail,+file,-all,+addr will cause hard errors for any non-network address recipient, unless you're running interactively or started with the option -~ or -#. in the latter case(s), any type may be used ๐
Okay so, the expandaddr thing is still going ๐
. Plain local user names can get expanded into fully qualified network addresses ๐ (also see hostname) if you slip nametoaddr into the list ๐ฆ. Historically, invalid recipients just got quietly stripped off with zero errors ๐, but you can change that by making failinvaddr an entry in the list (it really acts like failinvaddr,+addr) ๐. Then there's domaincheck (really domaincheck,+addr), which checks address domain names against a whitelist and strips out addressees that fail (fail for hard errors) ๐ซ. The domain localhost and the non-empty value of hostname (the real hostname otherwise) are always whitelisted ๐, and expandaddr-domaincheck can extend that list. Last but not least, some address providers (for example -b, -c and all other command line recipients) get evaluated as if they were specified within dollar-single-quotes (see โShell-style argument quotingโ) if the value list contains the string shquote โจ.
expandaddr-domaincheck ๐ธ
This can be set to a comma-separated list of domain names that should be whitelisted for the evaluation of the domaincheck mode of expandaddr. Heads up: IDNA encoding is not automatically performed, so addrcodec can be used to prepare the domain (of an address) ๐ง.
expandargv ๐
Unless this variable is set, extra mta (Mail-Transfer-Agent) arguments from the command line (the stuff that can be given after a -- separator) cause the program to terminate with failure status ๐จ. You can get the same effect by using the special (case-insensitive) value fail ๐
โโ๏ธ. A slightly less strict vibe is the otherwise identical restrict, which does accept those arguments in interactive mode, or if tilde commands were enabled explicitly by using one of the command line options -~ or -# ๐. The empty value allows unconditional usage ๐ฆ.
features ๐
(Read-only) This is a string giving a list of optional features โจ. Features get a plus sign + in front if they're available, and a hyphen-minus - otherwise ๐. To make substring matching easy, the string starts and ends with a comma ๐ซถ. The output of the command version includes this info in a much prettier format ๐
.
flipr ๐
(Boolean) This setting flips the meanings of a set of reply commands ๐. The lowercase variants, which by default address all recipients included in the header of a message (reply, respond, followup), become the uppercase variants, which by default address the sender only (Reply, Respond, Followup), and vice versa ๐. Iconic reversal, honestly ๐.
folder ๐
This is the default path under which mailboxes are saved ๐๏ธ. Filenames that begin with the plus sign + have the plus sign replaced with the value of this variable if it's set ๐ธ. Otherwise the plus sign stays unchanged when doing โFilename transformationsโ ๐ฆ. Also see folder for more on this topic, and know about the standard-imposed implications of outfolder ๐. The value supports a subset of transformations itself, and if the non-empty value does not start with a solidus /, then the value of HOME gets prefixed automatically ๐ . Once the actual value is evaluated, the internal variable folder-resolved gets updated for caching purposes ๐พ.
folder-hook-FOLDER, folder-hook ๐ช
This names a defined macro that gets called whenever a folder is opened โจ. The macro also gets invoked when new mail arrives, but message lists for commands executed from the macro only include the newly arrived messages then ๐ฌ. localopts are activated by default in a folder hook, which means the covered settings get reverted once you leave the folder again ๐.
The specialized form overrides the generic one if FOLDER matches the file that gets opened ๐ฏ. Unlike other folder specifications, the fully expanded name of a folder, without metacharacters, is used to avoid ambiguities ๐ง. However, if the mailbox resides under folder, then the usual + specification is tried in addition ๐โโ๏ธ. So if folder is โmailโ (and thus relative to the user's home directory), then /home/usr1/mail/sent gets tried as folder-hook-/home/usr1/mail/sent first, and then followed by folder-hook-+sent ๐.
folder-resolved ๐ง
(Read-only) This gets set to the fully resolved path of folder once that evaluation has happened ๐. It's pretty internal, so don't stress about it ๐.
followup-to ๐
(Boolean) This controls whether a Mail-Followup-To: header gets generated when sending messages to known mailing lists ๐จ. The user, as determined via from (or, if that contains multiple addresses, sender), will be placed in there if any list addressee is not a subscribed list ๐. Also see followup-to-honour and the commands mlist, mlsubscribe, reply and Lreply ๐ซถ.
followup-to-add-cc ๐ฏ
(Boolean) This controls whether the user gets added to the messages' Cc: list in addition to getting an entry in Mail-Followup-To: (see followup-to) ๐.
followup-to-honour ๐ฏ
This controls whether a Mail-Followup-To: header is honoured when group-replying to a message via reply or Lreply ๐. It's a โquadoptionโ; if set without a value, it defaults to โyesโ, and see followup-to ๐ธ.
forward-add-cc ๐ฎ
(Boolean) This decides whether senders of messages forwarded via ~F, ~f, ~m, ~U or ~u get made members of the carbon copies Cc: list ๐
.
forward-as-attachment ๐
(Boolean) Original messages are normally sent as inline text with the forward command, and only the first part of a multipart message is included ๐ฌ. With this setting enabled, messages get sent as unmodified MIME message/rfc822 attachments with all of their parts included ๐.
forward-inject-head, forward-inject-tail ๐
These are the strings to put before and after the text of a message with the forward command, respectively ๐ซ. The former defaults to -------- Original Message --------\n ๐. Special format directives in these strings get expanded if possible, and if so configured, the output gets folded according to quote-fold; for more, please refer to quote-inject-head ๐. Injections will not be performed by forward if the variable forward-as-attachment is set โ the โCOMMAND ESCAPESโ ~F, ~f, ~M, ~m, ~U, ~u always inject ๐จ.
from ๐
This is the address (or a list of addresses) to put into the From: field of the message header ๐. Quoting RFC 5322, these are the author(s) of the message, meaning the mailbox(es) of the person(s) or system(s) responsible for writing it ๐๏ธ. According to that RFC, the sender variable is required if from contains more than one address โ ๏ธ. [v15 behaviour may differ] Please expect automatic management of the from and sender relationship ๐ค. Depending on the context, these addresses get handled as if they were in the list of alternates ๐โโ๏ธ.
If a file-based MTA is used, then from (or, if that contains multiple addresses, sender) can still be used as the envelope sender address at the MTA protocol level (the RFC 5321 reverse-path) ๐ฌ. You can do that either via the -r command line option (without argument; see there for more), or by setting r-option-implicit ๐ง.
If the machine's hostname isn't valid on the Internet (for example, at a dialup machine), then either this variable orhostnamehas to be set ๐จ. ([v15-compat] an SMTP-based mta adds even more fine-tuning withsmtp-hostname.) If so, the message and MIME part related unique ID fieldsMessage-ID:andContent-ID:will be created, except when disallowed bymessage-id-disableorstealthmua๐.
fullnames ๐ชช
(Boolean) For historical reasons, comments and name parts of email addresses get removed by default when sending mail, replying to or forwarding a message ๐. If this variable is set, that stripping is not performed โจ.
fwdheading ๐ฐ๏ธ
[Obsolete] This is the predecessor of forward-inject-head ๐ต.
header ๐งพ
(Boolean) This causes the header summary to be written at startup and after commands that affect the number of messages or the order of messages in the current folder ๐. Unless in posix mode, a header summary also gets displayed on folder changes ๐. The command line option -N can be used to set noheader ๐
โโ๏ธ.
headline ๐
This is a format string to use for the summary of headers ๐จ. Format specifiers in the given string start with a percent sign % and may be followed by an optional decimal number indicating the field width ๐ข. If that number is negative, the field is left-aligned โฌ
๏ธ. Names and addresses are subject to modifications according to showname and showto ๐. Valid format specifiers are:
%%: a plain percent sign, nothing extra ๐%>: the dotmark ๐ธ. It's a space character, except for the current message (the "dot"), where it shows>. This depends onheadline-plain.%<: the other dotmark ๐ฆ. Same deal as%>, but for the current message it shows<. This also depends onheadline-plain.%$: [Option] the spam score of the message, as classified by the commandspamrate๐ฅ. If there's no spam support, you only get a replacement character ๐.%a: the message attribute character (status flag) ๐ท๏ธ. You can change what it shows by settingattrlist.%d: the date from theDate:header whendatefieldis set (that's the default) ๐ . Otherwise you get the date the message was received. You can format it by assigning astrftime(3)format string todatefield(anddatefield-markout-older).%e: the indenting level inthreaded sort mode ๐งต%f: the address of the message sender ๐%i: the message thread tree structure ๐ณ. Heads up, this one doesn't support a field width, and it honoursheadline-plain.%L: mailing list status ๐ฎ. Is the addressee a knownl(mlist) orLmlsubscribed mailing list? The letterPannounces that there's an RFC 2369List-Post:header, which makes the message a juicy target forLreply๐.%l: the number of lines in the message, if available ๐%m: message number ๐ข%o: the number of octets (bytes) in the message, if available ๐ฆ%S: the message subject (if any) in double quotes ๐%s: the message subject (if any), no quotes โจ%t: the position in threaded/sorted order ๐ง%U:0unless you're in an IMAP mailbox, where it expands to the UID of the message ๐
The default is %>%a%m %-18f %16d %4l/%-5o %i%-s, or %>%a%m %20-f %16d %3l/%-5o %i%-S if bsdcompat is set. Also see attrlist, headline-plain and headline-bidi ๐.
๐ headline-bidi
Okay, bidirectional text is a whole mood ๐ญ. In headers, numbers like dates or file sizes don't flip the current text direction, so arabic or other right-to-left text ends up with ugly, messy line layouts. The catch is that only a minority of terminals can handle direction changes properly, so you have to step in yourself for it to look good โจ. Also, extended host system support is required, and detection of the terminal character set is one precondition. This feature only works in a Unicode (UTF-8) locale ๐ซถ.
Setting this variable makes S-nail wrap the text fields in the headline (and some other fields, like dynamic expansions in the prompt) with special Unicode control sequences ๐. You can fine-tune how much terminal support it assumes by assigning a value:
- No value at all (or any value other than
1,2and3) makes S-nail assume your terminal can properly deal with Unicode version 6.3 ๐ . The text gets embedded in a pair of U+2068 (FIRST STRONG ISOLATE) and U+2069 (POP DIRECTIONAL ISOLATE) characters, and no space on the line is reserved for them. 1: Unicode 6.3 again, but this time room for two spaces gets reserved on the line for the control sequences ๐งธ.2: Unicode 1.1 support, using U+200E (LEFT-TO-RIGHT MARK) ๐ธ.3: also Unicode 1.1 support with U+200E (LEFT-TO-RIGHT MARK), but this one reserves room for two spaces on top of that ๐ฆ.
๐จ headline-plain
(Boolean) On Unicode (UTF-8) aware terminals, enhanced graphical symbols are used by default for certain headline entries. If you set this variable, only basic US-ASCII symbols will be used ๐.
๐ history-file
[Option] The (expandable) location of a permanent history file for the MLE line editor (see โOn terminal control and line editorโ). Also see history-size ๐.
๐ history-gabby
[Option] Adds more entries to the MLE history than usual. It takes a comma-separated list of case-insensitive strings, so you can fine-tune which gabby entries are allowed ๐ฃ๏ธ. If it contains errors, erroneous commands get added too. all adds all the optional entries, and it's the fallback chattiness identifier of on-history-addition ๐
.
๐ค history-gabby-persist
(Boolean)[Option] The history-gabby entries won't be saved in persistent storage unless this variable is set. Knowing whether a persistent entry was gabby isn't lost though. Also see history-file ๐.
๐ history-size
[Option] Setting this imposes a limit on the number of concurrent history entries. Set it to the value 0 and no further history entries get added. Loading and incorporating the history-file upon program startup can also be suppressed by doing this ๐ซ. Runtime changes won't be reflected until the history is saved or loaded (again) โณ.
๐ญ hold
(Boolean) This setting controls whether messages are held in the system inbox, and it's set by default โจ.
๐ hostname
Used instead of the value obtained from uname(3) and getaddrinfo(3) as the hostname when expanding local addresses, for example in From: (also see โOn sending mail, and non-interactive modeโ, for expansion of addresses that have a valid user-, but no domain name in angle brackets) ๐. If either from or this variable is set, the message and MIME part related unique ID fields Message-ID: and Content-ID: will be created, except when disallowed by message-id-disable or stealthmua ๐. If the [Option]al IDNA support is available (see idna-disable), variable assignment is aborted when a necessary conversion fails ๐ฅ.
Setting it to the empty string makes the normal hostname get used, but it still enables creation of said ID fields ๐ซง. [v15-compat] In conjunction with the built-in SMTP mta smtp-hostname, it also influences the results. So one should produce some test messages with the desired combination of hostname, and/or from, sender etc. first ๐งช.
๐ idna-disable
(Boolean)[Option] Can be used to turn off the automatic conversion of domain names according to the rules of IDNA (internationalized domain names for applications) ๐. Since the IDNA code assumes that domain names are specified with the ttycharset character set, a UTF-8 locale charset is required to represent all possible international domain names (before conversion, that is) ๐ซ.
โ๏ธ ifs
The input field separator that is used ([v15 behaviour may differ] by some functions) to decide where to split input data ๐ช.
- Unsetting it is treated as assigning the default value,
' \t\n'. - If set to the empty value, no field splitting will be performed at all ๐ .
- If set to a non-empty value, all whitespace characters are extracted and assigned to the variable
ifs-ws.- a.
ifs-wswill be ignored at the beginning and end of input. Diverging from POSIX shells default whitespace is removed in addition, which is owed to the entirely different line content extraction rules ๐งน. - b. Each occurrence of a character of
ifswill cause field-splitting, and any adjacentifs-wscharacters will be skipped โจ.
- a.
๐ ifs-ws
(Read-only) Automatically deduced from the whitespace characters in ifs ๐ค.
๐ ignore
(Boolean) Ignores interrupt signals from the terminal while you're entering messages. Instead, they get echoed as @ characters and the current line gets discarded ๐.
๐ซ ignoreeof
(Boolean) Ignores end-of-file conditions (control-D) in โCompose modeโ on message input and in interactive command input ๐. If set, an interactive command input session can only be left by explicitly using one of the commands exit and quit. Message input in compose mode can only be terminated by entering a period . on a line by itself, or by using ~. from โCOMMAND ESCAPESโ. Setting this implies the behaviour that dot describes in posix mode ๐ซถ.
๐ฌ inbox
If this is set to a non-empty string, it specifies the user's โprimary system mailboxโ ๐. It overrides MAIL and the system-dependent default, and (thus) gets used to replace % when doing โFilename transformationsโ. Also see folder for more on this topic ๐. The value supports a subset of transformations itself ๐.
๐ indentprefix
String used by the ~m, ~M and ~R โCOMMAND ESCAPESโ, and by the quote option, for indenting messages. It replaces the POSIX mandated default tabulator character '\t' ๐. Also see quote-chars ๐.
keep ๐
(Boolean) okay so, if this is set, an empty โprimary system mailboxโ file doesn't get deleted ๐งธ ngl this is so important. With posix mode on, any empty file gets removed unless this variable is set. It can help other mail user agents play nicely when you're all sharing one folder directory ๐ซถ, and it stops sneaky people from making fake mailboxes in a world-writable spool directory ๐. [v15 behaviour may differ] Only local regular (MBOX) files are protected. Maildir and other mailbox types are never removed, even when they're empty โจ.
keep-content-length ๐
(Boolean) when you're editing messages and writing MBOX mailbox files, S-nail can keep the Content-Length: and Lines: header fields that some MUAs generate, as long as you set this variable ๐ฆ. S-nail doesn't use or update these non-standardized fields (which honestly shows one of their conceptual problems ๐
), so stripping them should make things more interoperable between MUAs that work with the same mailbox files ๐ธ. Heads up: if this isn't set but writebackedited is, the automatic stripping that happens will already mark the message as modified ๐. [v15 behaviour may differ] Someday S-nail will be able to rewrite and apply a MIME encoding to modified messages, and then those fields will get stripped silently ๐ง.
keepsave ๐
(Boolean) when you save a message, it's normally thrown out of the folder it came from once you quit S-nail. This setting keeps every saved message around, so nothing gets tossed ๐ซถ.
line-editor-cpl-word-breaks โญ
[Option] this is the list of bytes that the mle-complete tabulator completion uses to figure out where word boundaries are. By default it's "'@=;|: ๐ [v15 behaviour may differ] This mechanism is still pretty limited, so don't expect miracles yet ๐.
line-editor-disable ๐
(Boolean) turns off any line editing capabilities from S-nail's POW ๐ . Check out โOn terminal control and line editorโ if you want the full tea โ.
line-editor-no-defaults ๐ฆ
(Boolean)[Option] this one stops S-nail from setting up any default key bindings, so you're on your own with the keys ๐ฅ.
log-prefix ๐
Error log messages get this prefix string, which is s-nail: ๐ (yes, there's a trailing space, it's giving precision โจ).
mailbox-display ๐ธ
(Read-only) this is the name of the current mailbox (folder), possibly shortened so it's easier to read on screen ๐.
mailbox-resolved ๐ง
(Read-only) this is the fully resolved path of the current mailbox, no shortcuts, no drama ๐บ๏ธ.
mailcap-disable ๐
(Boolean)[Option] turns off using MIME type handlers from the โThe Mailcap filesโ, and stops them from being loaded automatically ๐ โโ๏ธ.
mailx-extra-rc ๐
This is an extra startup file that gets loaded last, after the โResource filesโ ๐งธ. Put commands in here that other POSIX mailx(1) implementations don't understand, which basically means anything not covered by โInitial settingsโ ๐ .
markanswered โจ
(Boolean) when you reply to a message and this variable is set, that message gets marked as answered ๐. See the section โMessage statesโ for more ๐ฆ.
mbox-fcc-and-pcc ๐ฅ
(Boolean) by default, all file and pipe message receivers (see expandaddr) get valid MBOX database entry message data (see folder, mbox-rfc4155), and existing file targets get extended so they follow RFC 4155 ๐ซถ. If you unset this, you get a plain standalone RFC 5322 message instead, and existing file targets get overwritten ๐ญ so be careful!
mbox-rfc4155 ๐
(Boolean) when S-nail opens MBOX mailbox databases, it uses the super tolerant POSIX rules to find message boundaries (the so-called From_ lines) instead of the stricter rules from RFC 4155 ๐
. This is for compatibility with old software ๐ง. You can flip this behaviour by setting this variable โจ.
This can be handy temporarily when S-nail complains about invalid From_ lines while opening an MBOX ๐. Set this variable, reopen the mailbox in question, and it might fix things ๐ฉน. If it does, copying the whole mailbox to another file, like copy * SOME-FILE, will do proper, all-compatible From_ quoting for every message it detects, and you'll end up with a valid MBOX mailbox ๐. [v15 behaviour may differ] The better, non-destructive approach is to re-encode invalid messages as if they were created anew, instead of mangling the From_ lines ๐. That needs the structural code changes from the v15 rewrite ๐ ๏ธ. Finally, you can unset the variable again when you're done ๐งน:
define mboxfix {
localopts yes; wysh set mbox-rfc4155;\
wysh File "${1}"; copy * "${2}"
}
call mboxfix /tmp/bad.mbox /tmp/good.mbox
memdebug ๐งช
(Boolean) this is an internal development variable ๐ ๏ธ. It keeps memory debugging enabled even when debug isn't set ๐.
message-id-disable ๐ฆ
(Boolean) setting this variable completely suppresses the generation of the Message-ID: and Content-ID: headers for messages and MIME parts ๐ธ. That basically leaves the job up to the mta (Mail-Transfer-Agent) or the SMTP server ๐ซถ. Just a heads up: RFC 5321 doesn't require an SMTP server to add this field itself, so make sure yours accepts messages without a Message-ID ๐.
message-inject-head ๐
A string that goes at the very beginning of each new message, followed by a newline โจ. [Obsolete] This one is on its way out ๐ฅ. The escape sequences tabulator \t and newline \n are understood, but use the wysh prefix when you set the variable(s) instead ๐
.
message-inject-tail ๐
A string that goes at the very end of each new message, followed by a newline ๐. [Obsolete] Also on its way out ๐ฅ. The escape sequences tabulator \t and newline \n are understood, but use the wysh prefix when you set the variable(s) instead ๐
. Also see on-compose-leave ๐ฆ.
metoo ๐
(Boolean) usually, when an alias expansion includes the sender, the sender gets removed from the expansion ๐
โโ๏ธ. Setting this option stops those removals ๐ซถ. Heads up: a set metoo also passes a -m option through to the mta (Mail-Transfer-Agent) ๐. Most modern MTAs don't document this flag anymore, but no MTA is known to not support it (for historical compatibility) ๐ง.
mime-allow-text-controls ๐
(Boolean) when you send messages, every part gets MIME-inspected so S-nail can figure out the Content-Type: and Content-Transfer-Encoding: it needs to travel over mail transport (see mime-encoding) ๐. It's kind of like what file(1) does with the --mime option ๐ง.
Here's the drama ๐ญ: this classification treats text files encoded in UTF-16 (which you'll see a lot with HTML files) and similar character sets as binary octet-streams. It forcefully changes any text/plain or text/html spec to application/octet-stream ๐. When that happens, a charset MIME parameter that wasn't set yet gets set to binary, which makes it impossible for the receiving MUA to automatically interpret the contents ๐.
If you set this variable, and the data was unambiguously identified as text at first glance (through a .txt or .html file extension), then the original Content-Type: won't be overwritten ๐ซถ.
mime-alternative-favour-rich ๐ธ
(Boolean) if this is set, rich MIME alternative parts (like HTML) get preferred over the plain text versions included with them when messages are displayed ๐ . This only happens when a handler exists that produces output which can be (re)integrated into S-nail's normal visual display โจ.
mime-counter-evidence ๐ง
Normally, the Content-Type: field decides how MIME parts get handled ๐ฆ. But some MUAs don't use โThe mime.types filesโ (also see โHTML mail and MIME attachmentsโ) or anything similar to classify content properly ๐. Instead they label even plain text attachments as the unspecific application/octet-stream ๐ค. If this variable is set, S-nail will try to re-classify those MIME parts when it can, for example through a possibly existing attachment filename ๐ต๏ธโโ๏ธ.
A non-empty value can also be given, and it should be a number that acts as a carrier of bits ๐งฎ. It's best specified as a binary value, like 0b1111 ๐ค. Here's what each bit does:
- Bit two (counting from 1, decimal 2): the detected mimetype gets carried along with the message and used to decide which MIME handler to use ๐ฆ. When you display that MIME part, the part-info shows the overridden content-type with a plus sign
+โ. - Bit three (decimal 4): the counter-evidence is always produced, and a positive result gets used as the MIME type ๐. It even forcefully overrides the part's given MIME type ๐ฅ.
- Bit four (decimal 8): as a last resort, the actual content of
application/octet-streamparts gets inspected, so data that looks like plain text can be treated as plain text ๐. This mode is even more relaxed when the data is displayed to the user or used as a message quote ๐ซ . (The data consumers that mangle data for display purposes, like masking control characters, are a good example ๐.)
mime-encoding ๐
This is the MIME Content-Transfer-Encoding to use in outgoing text messages and message parts, wherever it applies ๐. Plain 7-bit clean text messages go without an encoding whenever that's possible โจ:
8bit(or8b) ๐ซฃ: this one just passes your raw data straight through, totally unchanged โจ. It's lowkey risky, though, because it can cause problems when mail goes over channels that aren't ESMTP (RFC 1869) compliant. A few input data constructs aren't allowed by the specs either, and they might make a different transfer-encoding get picked instead ๐. By established rules and popular demand, occurrences of^From_(see mbox-rfc4155) get MBOXO quoted, meaning they're prefixed with a greater-than sign>. That happens instead of switching to a non-destructive encoding likequoted-printable, unless something like message signing needs otherwise ๐ฅ.quoted-printable(orqp) ๐: this one is 7-bit clean, and ASCII characters pass through untouched, so an English message can be read as-is ๐. It's also fine for other single-byte locales that share a lot of characters with ASCII, likeISO-8859-1. The catch is the overhead for other character sets. It can take up to twelve (12) bytes to encode a single four (4) byte UTF-8 character, which is a lot ๐ญ. This is the default encoding ๐.base64(orb64) ๐ง: also 7-bit clean, and it's always used for binary data ๐ฆ. It has a constant input:output ratio of 3:4, so no matter the character set, three bytes of input become four bytes of output. It's not human readable without performing a decoding step ๐.
mime-force-sendout
(Boolean) [Option] ๐ For when failing to send is simply not an option because of non-convertible character content. Set this variable and, as a last resort, the part content gets classified as application/octet-stream. For the full tea on character set conversion, check the section โCharacter setsโ, and for handling part content internally or externally, check โHTML mail and MIME attachmentsโ ๐
.
mimetypes-load-control
This is your control panel for which โThe mime.types filesโ get loaded ๐๏ธ. If the letter u is in the value, your personal ~/.mime.types file gets loaded (if it exists). The letter s does the same job for the system wide /etc/mime.types. Directives in the user file take precedence, and letter matching is case-insensitive ๐ง. If this variable isn't set, S-nail will try to load both files. The S-nail-built-in MIME types can't be suppressed, but they get matched last. You can list the order with mimetype ๐ธ.
Want more sources? Use a different syntax. If the value string contains an equals sign =, it's parsed as a comma-separated list of those letters plus f=FILENAME pairs. The given filenames get expanded and loaded, and their content may use the extended syntax described in the section โThe mime.types filesโ โจ. Directives found in those files always take precedence, meaning they're prepended to the MIME type cache ๐.
mta
Pick an alternate Mail-Transfer-Agent by giving either the full pathname of an executable (a file:// prefix is optional), or, [Option]ally, a SMTP aka SUBMISSION protocol URL [v15-compat]:
submissions://[user[:password]@]server[:port]
([no v15-compat]: [smtp://]server[:port].) The default is chosen at compile time ๐ง. MTA data transfers always run in asynchronous child processes, and they go unsupervised unless the sendwait or the verbose variable is set ๐. Also see mta-bcc-ok. [Option]ally, you can do expansion of aliases(5) by setting mta-aliases ๐.
For testing, there's the test pseudo-MTA ๐งช. It dumps to standard output, or optionally to a file, and it honours mbox-fcc-and-pcc:
$ echo text | s-nail -:/ -Smta=test -s ubject ex@am.ple
$ </dev/null s-nail -:/ -Smta=test://./xy ex@am.ple
For a file-based MTA, you might need to set mta-argv0 in order to choose the right target of a modern mailwrapper(8) environment ๐. It gets command line arguments from a few possible sources: from the variable mta-arguments if it's set, and from the command line if one is given and the variable expandargv allows their use. Argument processing of the MTA ends with a -- separator ๐.
You can turn off the implicit MTA command line arguments by setting the boolean variable mta-no-default-arguments. That also disables passing -- to the MTA ๐
โโ๏ธ. The ones it covers are -i (for not treating a line with only a dot . character as the end of input), -m (shall the variable metoo be set), and -v (if the verbose variable is set). In conjunction with the -r command line option or r-option-implicit -f, as well as possibly -F, these will (not) be passed.
โ ๏ธ Heads up: the SMTP and SUBMISSION protocols are a whole thing, so read these variants carefully ๐.
[Option]ally, S-nail can send mail over SMTP aka SUBMISSION network connections to a single defined smart host ๐ฎ. Do this by setting this variable to a SMTP or SUBMISSION URL (see โOn URL syntax and credential lookupโ). You can specify an authentication scheme via the variable chain smtp-auth ๐. Encrypted network connections are [Option]ally available, and the section โEncrypted network communicationโ gives an overview with links to more info ๐. With some mail providers, you may need to set the smtp-hostname variable in order to use a specific combination of from, hostname and mta ๐ซถ. Network communication socket timeouts are configurable via socket-connect-timeout โฑ๏ธ. All generated network traffic may be proxied over a SOCKS socks-proxy, and it can be logged by setting verbose twice ๐. The following SMTP variants may be used:
- The plain SMTP protocol (RFC 5321) ๐ฌ normally lives on server port 25 and requires setting the smtp-use-starttls variable to enter a TLS encrypted session state. Assign a value like [v15-compat]
smtp://[user[:password]@]server[:port]([no v15-compat]smtp://server[:port]) to choose this protocol. - SMTPS ๐ is supposed to live on server port 465 and is automatically TLS secured. Unfortunately, it never became a standardized protocol, so your host's network service database may not support it. In fact, the port number has already been reassigned to other protocols! ๐ It's still a commonly offered protocol, though, so you can choose it by assigning a value like [v15-compat]
smtps://[user[:password]@]server[:port]([no v15-compat]smtps://server[:port]). Because of the problems mentioned, it's usually necessary to explicitly specify the port as:465, however ๐. - The SUBMISSION protocol (RFC 6409) ๐จ lives on server port 587 and is identical to the SMTP protocol from S-nail's point of view. It requires setting smtp-use-starttls to enter a TLS secured session state. For example: [v15-compat]
submission://[user[:password]@]server[:port]. - The SUBMISSIONS protocol (RFC 8314) ๐ฆ lives on server port 465 and is TLS secured by default. You can choose it by assigning a value like [v15-compat]
submissions://[user[:password]@]server[:port]. Because of the SMTPS problems above, and because SUBMISSIONS is new and a successor that lives on the same port as the historical engineering mismanagement named SMTPS, it's usually necessary to explicitly specify the port as:465๐ฅฒ.
mta-aliases
[Option] If this is set to a path pointing to a text file in valid MTA (Postfix) aliases(5) format, the file gets loaded and cached (manageable with mtaaliases) ๐. From then on, plain name receiver names (see expandaddr) get recursively expanded as a last expansion step, after the distribution lists that can be created with alias ๐งฉ. There are some constraints on aliases(5) content. Only local addresses (names) that are valid usernames ([a-z_][a-z0-9_-]*[$]?) are treated as expandable aliases. [v15 behaviour may differ] :include:/file/name directives are not supported ๐
โโ๏ธ. By including -name in expandaddr, you can assert that only expanded names (mail addresses) are passed through to the MTA ๐ซก.
mta-arguments
These are arguments to pass through to a file-based mta (Mail-Transfer-Agent) ๐ฆ. They're parsed according to โShell-style argument quotingโ into an array of arguments, which gets joined onto MTA options from other sources. For example: ? wysh set mta-arguments='-t -X "/tmp/my log"' ๐.
mta-no-default-arguments
(Boolean) This one avoids passing standard command line options to a file-based mta (please see there) ๐โจ.
mta-no-receiver-arguments (Boolean)
okay so by default, every receiver address gets passed as a command line option to a file-based mta ๐ฎ. Setting this variable turns that off, which helps MTAs that give those arguments special treatment ๐ซถ. Just know that turning it off can mean you need to pass a -t through mta-arguments so the MTA knows to use the passed message as a template โจ.
mta-argv0
Lots of systems use a so-called mailwrapper(8) setup to stay compatible with sendmail(1) ๐ง. It works by checking the name that was used to invoke the mail delivery system. If this variable is set, the mailwrapper (the program that actually runs when you call the file-based mta) will treat its contents as that name ๐
.
mta-bcc-ok (Boolean)
this one is a little messy ๐ญ. In violation of RFC 5322, some MTAs don't remove Bcc: header lines from transported messages after they've noted the receivers. (Exim and Courier, for example, need the command line option -t to actually enforce removal ๐.) Unless this is set, and the corresponding receivers are addressed only by protocol-specific means or MTA command line options, the header itself gets stripped before the message is sent over the wire ๐ฆ.
netrc-lookup-USER@HOST, netrc-lookup-HOST, netrc-lookup
(Boolean)[v15-compat][Option] This one controls whether your ~/.netrc file gets used to look up account credentials ๐. It's covered in the section โOn URL syntax and credential lookupโ and for the command netrc. The section โThe .netrc fileโ documents the file format if you want the deets. Also see netrc-pipe.
netrc-pipe
[v15-compat][Option] Okay this is so clever ๐ง . When ~/.netrc is loaded (see netrc and netrc-lookup), S-nail will read the output of a shell pipe instead of your ~/.netrc file, but only if this is set to the desired shell command. This is how you could, for example, store your ~/.netrc in encrypted form ๐:
? set netrc-pipe='gpg -qd ~/.netrc.pgp'
newfolders
[Option] If this is set to maildir, newly created local folders will be Maildir instead of MBOX ๐.
newmail
Checks for new mail in the current folder every time the prompt shows up ๐. A Maildir folder has to be re-scanned to figure out if new mail has arrived. If you set this to the special value nopoll, a Maildir folder won't get rescanned completely. Instead, only timestamp changes get detected. Maildir folders are [Option]al ๐ธ.
outfolder (Boolean)
When this is on, a non-absolute filename given to record, plus the sender-based filenames used by the Copy, Save, Followup and followup commands, get interpreted relative to the folder directory instead of the current directory ๐.
on-account-cleanup-ACCOUNT, on-account-cleanup
This is a macro hook that runs once you leave an account, as the very last step before the per-account localopts get unrolled ๐งน. It runs even if there's a fatal error, including ones caused by switching to the account in the first place ๐. Keep it to only the absolutely necessary stuff, like cleaning up alternates, for example. The specialized form wins over the generic one if it's found ๐.
on-compose-cleanup
This is a macro hook that gets called after the message has been sent (or not, if something failed) ๐จ. It's the very last step before the compose mode localopts get unrolled. It runs even on fatal errors, so again, only the absolutely necessary actions, like cleaning up alternates, for example ๐ซถ.
For compose mode hooks that might affect the message content, check out on-compose-enter, on-compose-leave, and on-compose-splice.
[v15 behaviour may differ] This hook exists because things likealias,alternates,commandalias, andshortcut(to name a few) aren't covered bylocaloptsor bylocal:. That means changes made in compose mode keep sticking around afterwards ๐ค.
on-compose-enter, on-compose-leave
These are macro hooks that get called once compose mode is entered, and after composing has been finished, respectively ๐. The exact order of steps is documented for ~., which is one of the โCOMMAND ESCAPESโ ๐. You can query context about the message you're working on via digmsg. localopts are enabled for these hooks, and any variable changes get forgotten after the message has been sent. Use on-compose-cleanup for any other cleanup steps ๐งฝ.
Here's an example that injects a signature via message-inject-tail. Honestly, using on-compose-splice to simply inject the file you want via ~< or ~<! might be a better approach ๐
.
define t_ocl {
vput ! i cat ~/.mysig
if $? -eq 0
vput csop message-inject-tail trim-end $i
end
# Alternatively
readctl create ~/.mysig
if $? -eq 0
readall i
if $? -eq 0
vput csop message-inject-tail trim-end $i
end
readctl remove ~/.mysig
end
}
set on-compose-leave=t_ocl
on-compose-splice, on-compose-splice-shell
These hooks run once the normal compose mode is finished, but before the on-compose-leave macro hook and friends get called ๐ฌ. Both hooks run in a subprocess, with their input and output connected to S-nail so they can act as if they were an interactive user ๐ค. The difference between them is that the latter is a SHELL command, whereas the former is a normal defined macro. The macro is restricted to a small set of commands, though (the verbose output of, for example, list will indicate said capability). localopts are enabled for these hooks (in the parent process), which means any setting gets forgotten after the message has been sent. Use on-compose-cleanup for any other cleanup as necessary ๐งน.
During these hooks, S-nail temporarily forgets whether it has been started in interactive mode ๐. A restricted set of โCOMMAND ESCAPESโ will always be available, and for guaranteed reproducibility, escape and ifs will be set to their defaults. The compose mode command ~^ has been especially designed for scriptability via these hooks ๐ง. The first line the hook reads on its standard input is the protocol version of said command escape, currently 0 0 2.
Backward incompatible protocol changes have to be expected, so keep an eye on the version ๐.
Be careful with deadlocks! Avoid false control flow: if both involved processes wait for more input at the same time, or one doesn't expect more input while the other is stuck waiting to consume its output, etc., things will hang ๐.
There's no automatic synchronization of the hook. It won't be stopped automatically just because it, e.g., emits ~x. The hooks will receive a termination signal if the parent enters an error condition. [v15 behaviour may differ] Protection against and interaction with signals is not yet given. It's likely that in the future these scripts will be placed in an isolated session, which gets signalled in its entirety as necessary ๐ฆ.
Here's the macro version of the splice hook:
define ocs_signature {
read version
echo '~< ~/.mysig' # '~<! fortune pathtofortunefile'
}
set on-compose-splice=ocs_signature
And here's the shell version, which uses wysh to set it:
wysh set on-compose-splice-shell=$'\
read version;\
printf "hello $version! Headers: ";\
echo \'~^header list\';\
read status result;\
echo "status=$status result=$result";\
'
And here's a more involved macro that reads the header list and inserts a Cc: header if needed ๐:
define ocsm {
read version
echo Splice protocol version is $version
echo '~^h l'; read hl; vput csop es subs "${hl}" 0 1
if "$es" != 2
echoerr 'Cannot read header list'; echo '~x'; xit
endif
if "$hl" !%?case ' cc'
echo '~^h i cc "Diet is your <mirr.or>"'; read es;\
vput csop es substring "${es}" 0 1
if "$es" != 2
echoerr 'Cannot insert Cc: header'; echo '~x'
# (no xit, macro finishes anyway)
endif
endif
}
set on-compose-splice=ocsm
on-history-addition
This hook gets called if an entry is about to be added to the history of the MLE, as documented in โOn terminal control and line editorโ ๐. It's called with three arguments. The first is the name of the input context (see bind). The second is either an empty string or the matching history-gabby type. The third is the complete command line that's about to be added. The entry won't be added to history if the hook returns non-0 ๐ซ.
[v15 behaviour may differ] A future version will give the expanded command name as the third argument, followed by the tokenized command line as parsed in the remaining arguments. The first of those is the original unexpanded command name. So one may doshift 4and will then be able to access the positional parameters as usual via*,#,1etc. ๐ฎ
on-main-loop-tick
This hook gets called whenever the program's main event loop is about to read the next input line ๐.
Heads up! Variable and other changes it performs are not scoped as via localopts! ๐
on-program-exit ๐ชโจ this hook fires when the program exits, whether you bail with exit or quit, or because send mode is finished. โ ๏ธ heads up, it runs super late, so the terminal settings and stuff are already torn down by then ๐ญ
on-resend-cleanup๐งน [v15 behaviour may differ] basically the same as on-compose-cleanup, except it only goes off when youresend. ๐on-resend-enter๐ [v15 behaviour may differ] same energy as on-compose-enter, but again, only triggered byresend. Right now there's no digmsg support for it, for example. ๐page๐ (Boolean) if this is set, every message fed through the pipe command gets a formfeed character\fafter it. Very organized of you ๐ธpassword-USER@HOST,password-HOST,password๐ [v15-compat] this is a variable chain that sets a password. It only gets used when there isn't one already given in the protocol or the account-specific URL. As a last resort, S-nail will ask you for the password on your own terminal if the auth method needs one. โ ๏ธ Storing passwords in a startup file is generally a security risk, so that file should only be readable by the user who's running the thing. Protect your secrets, bestie ๐ซถpassword-USER@HOST๐๏ธ [no v15-compat] (see the chain above for [v15-compat]) this sets the password forUSERwhen connecting toHOST. If there's no variable defined for that host, you'll get asked for the password on standard input. Same warning applies: passwords in a startup file are a security risk, so keep the file readable by the invoking user only. ๐คpiperaw๐ฆ (Boolean) this sends messages to the pipe command without doing any MIME or character set conversions. Raw and unfiltered, iconic ๐pipe-EXTENSION๐งฉ this is identical to pipe-TYPE/SUBTYPE, exceptEXTENSION(normalized to lowercase using the ASCII charset's character mappings) is a file extension instead, likexhtml. Handlers registered this way take precedence, so they're the main character here ๐pipe-TYPE/SUBTYPE๐๏ธ okay so, when a MIME message part is identified asTYPE/SUBTYPE(case-insensitive, normalized to lowercase using ASCII character mappings), it gets displayed or quoted, and its text is filtered through this variable's value, which gets interpreted as a shell command. Unless noted otherwise, only parts you can display as inline plain text (see copiousoutput) are covered. Every other MIME part only gets considered by and for mimeview. ๐
The special value question mark ? forces the message part to be interpreted as plain text, like set pipe-application/xml=?. (You can also get this by adding a MIME type-marker via mimetype.) [Option]ally, MIME type handlers can be defined in "The Mailcap files", which is where you should look for docs on flags like copiousoutput. The question mark is also a trigger character that tells S-nail a set of flags is coming, which adjust how the rest of the value (the shell command) behaves. For example:
? set pipe-X/Y='?!++=? vim ${MAILX_FILENAME_TEMPORARY}'
*โป๏ธ the command output can be reintegrated into this MUA's normal processing: copiousoutput. Implied when using a plain โโ.#๐ only use this handler for display, not for quoting a message: x-mailx-noquote.&๐โโ๏ธ run the command asynchronously and don't wait for the handler to exit: x-mailx-async. The standard output of the command goes to /dev/null.!๐ฅ๏ธ the command has to run on an interactive terminal, and the terminal gets temporarily released so it can run: needsterminal.+๐ requests creation of a zero-sized temporary file. Its absolute pathname is made accessible through the environment variableMAILX_FILENAME_TEMPORARY: x-mailx-tmpfile. If you give it twice, S-nail unlinks the file automatically when the command loop is entered again at the latest: x-mailx-tmpfile-unlink. โ ๏ธ It's an error to use automatic deletion together with x-mailx-async, so don't mix those up ๐=โ๏ธ normally the MIME part content gets passed to the handler through standard input, but with this flag the data is written intoMAILX_FILENAME_TEMPORARYinstead (x-mailx-tmpfile-fill). Creating that file is implied. To get the temporary file automatically deleted, you still need two plus signs++.t๐ text type-marker: displays this as normal plain text (see "The mime.types files" for type-markers). It's identical to just giving a plain?, and it implies copiousoutput.h๐ [Option] HTML type-marker: displays via the built-in HTML-to-text filter. Implies copiousoutput.?๐ another question mark forcefully ends the interpretation of the remaining characters, so there's no confusion with normal shell command content. (Any character that isn't in this list has the same effect, fr fr.)
Some info about the MIME part being displayed gets embedded into the environment of the shell command:
MAILX_CONTENT๐ท๏ธ the MIME content-type of the part, if it's known. Otherwise it's just the empty string.MAILX_CONTENT_EVIDENCE๐ if mime-counter-evidence includes the carry-around-bit (2), this gets set to the detected MIME content-type. Otherwise it's only identical toMAILX_CONTENT, not always.MAILX_EXTERNAL_BODY_URL๐ MIME parts of typemessage/external-body access-type=urlstore their access URL in this variable. It's empty otherwise. โ ๏ธ URL targets should not be activated automatically, without supervision, so don't just click stuff blindly ๐ญMAILX_FILENAME๐ the filename, if one is set. Otherwise it's the empty string.MAILX_FILENAME_GENERATED๐ฒ a random string.MAILX_FILENAME_TEMPORARY๐๏ธ if temporary file creation was requested through the command prefix, this variable gets set and contains the absolute pathname of the temporary file.
pop3-auth-USER@HOST,pop3-auth-HOST,pop3-auth๐ [Option][v15-compat] this variable chain sets the POP3 authentication method. The supported ones are the defaultplain, [v15-compat]oauthbearer(see the "FAQ" entry "But, how about XOAUTH2 / OAUTHBEARER?"), and also [v15-compat]externalandexternanon, which are for TLS secured connections that pass a client certificate via tls-config-pairs. There may also be the [Option]al method [v15-compat]gssapi.externanondoesn't need any user credentials at all.externalandgssapineed a user, and the remaining ones also require a password. โจexternanononly builds on the credentials passed through a client certificate, and it's usually the way to go, since tested servers don't actually follow RFC 4422 and fail if you pass extra credentials. Unless pop3-no-apop is set, theplainmethod will [Option]ally get replaced with APOP if that's possible (see there). ๐งpop3-bulk-load-USER@HOST,pop3-bulk-load-HOST,pop3-bulk-load๐ฆ (Boolean)[Option] when S-nail accesses a POP3 server, it loads the headers of the messages and only requests the message bodies when you ask for them. For POP3 that means the message headers get downloaded twice ๐ฉ If this variable is set, S-nail downloads only complete messages from the given POP3 server(s) instead. Efficient bestie behavior ๐pop3-keepalive-USER@HOST,pop3-keepalive-HOST,pop3-keepaliveโฐ [Option] POP3 servers close the connection after a period of inactivity. The standard says this has to be at least 10 minutes, but practical experience may vary, so don't trust it blindly ๐ Setting this variable to a numeric value greater than0makes S-nail send aNOOPcommand every value seconds if no other operation is performed. Keeping the vibe alive ๐ซถpop3-no-apop-USER@HOST,pop3-no-apop-HOST,pop3-no-apop๐ซฃ (Boolean)[Option] unless this variable is set, the MD5 basedAPOPauthentication method gets used instead of your chosenplainpop3-auth when you connect to a POP3 server that advertises support for it. The advantage ofAPOPis that only a single packet gets sent for the user/password tuple. (Originally it was also because the password isn't sent in clear text over the wire, but MD5 doesn't offer sufficient security anymore, and today transport is almost always TLS secured.) Note that pop3-no-apop-HOST requires [v15-compat].pop3-use-starttls-USER@HOST,pop3-use-starttls-HOST,pop3-use-starttls๐ (Boolean)[Option] this causes S-nail to issue aSTLScommand, which makes an unencrypted POP3 session TLS encrypted. Not every server supports this, and it isn't used if the session is already encrypted by the POP3S method. Note that pop3-use-starttls-HOST requires [v15-compat].posix๐ท (Boolean) this flag enables POSIX mode, which changes the behaviour of S-nail wherever it deviates from standardized behaviour. It's automatically squared with the environment variablePOSIXLY_CORRECT, so changing one will adjust the other, periodt ๐ The following behaviour is covered and enforced by this mechanism:- Non-interactive mode ๐ฅบ means any error while loading resource files at startup makes the program exit. In interactive mode, the same errors only stop loading of the currently loaded (stack of) file(s, i.e., recursively). You can dodge these exits on a per-command basis with
ignerr, one of the โCommand modifiersโ ๐ฆ, on each command that's allowed to fail. alternates๐ replaces the list of alternate addresses instead of adding to it. It's only honoured for message replies of any sort, and for aliases.- The variable-inserting โCOMMAND ESCAPESโ
~A,~a,~Iand~iwill expand the embedded character sequences\t(horizontal tab) and\n(line feed) โจ. [v15 behaviour may differ] For compatibility reasons this step always runs, no exceptions. - Reading in messages via
~f(โCOMMAND ESCAPESโ) uses thetypeheaderpick selection, notforward๐ธ. - When you switch the active folder, no header summary gets shown, even if
headeris set ๐. - Setting
ignoreeofimplies the behaviour described bydot๐ซถ. - The
keepvariable now covers any empty mailbox, not just empty โprimary system mailboxโes. Empty mailboxes get removed when they're left that way ๐งน. - Each command has an exit
?and error!status that overwrites the status of the last command. InPOSIXmode the program exit status signals failure regardless, unless every message went out to the mta successfully. Also check outsendwait๐.
๐ print-alternatives
(Boolean) Okay so, when a MIME message part of type multipart/alternative gets displayed and has a subpart of type text/plain, the other parts usually get tossed ๐๏ธ. Setting this variable makes every subpart show up, just like the surrounding part was multipart/mixed ๐.
๐ prompt
This is the string used as your prompt in interactive mode ๐
. Every time the variable gets evaluated, its value is treated as if it were wrapped in dollar-single-quotes (see โShell-style argument quotingโ). That second expansion is what lets you embed status info, like ?, !, account or mailbox-display โจ.
Want characters to be ignored when the visual width of the result is calculated? Wrap them in a pair of reverse solidus escaped brackets: \[\E[0m\]. There's also a slot for coloured prompts through the [Option]al command colour ๐. And if you don't want any prompting at all, set this to the null string (aka set noprompt) ๐.
๐ฆ prompt2
Secondary prompts use this string. Otherwise it's identical to prompt. The default is .. ๐ง.
๐ธ quiet
(Boolean) Suppresses printing the version when the program is first invoked ๐คซ.
๐
quote
If this is set, messages processed by the variants of followup and reply start with the original message ๐ฌ. Lines of it get prefixed by indentprefix, taking quote-chars and quote-fold into account ๐.
No headers get quoted when it's set without a value, or set to noheading. Set to headers, the type headerpick selection gets included in the quote. allbodies embeds the (body) contents of all MIME parts, and allheaders also includes every header ๐. The quoted message gets wrapped by the expansions of quote-inject-head and quote-inject-tail ๐ซถ.
Also check out quote-add-cc, quote-as-attachment and ~Q, one of the โCOMMAND ESCAPESโ ๐ฅ.
๐ quote-add-cc
(Boolean) Decides whether the senders of messages quoted via ~Q become members of the carbon copies Cc: list ๐.
๐ quote-as-attachment
(Boolean) When you reply to a message, this adds the original message in its entirety as a message/rfc822 MIME attachment ๐. It works regardless of how quote is set ๐.
๐ quote-chars
This can be set to a string made of non-whitespace ASCII characters, which get treated as quotation leaders. The default is >|}: ๐.
โจ quote-fold
[Option] This can be set alongside indentprefix for a fancier quotation ๐ซ. Leading quotation characters (quote-chars) get compressed, and overlong lines get folded โ๏ธ.
quote-fold takes one, two or three space-separated numeric values. They're read as the maximum (goal) and the minimum line length, respectively. It's kind of like the fmt(1) program, but line-based instead of paragraph-based ๐ง . The third value is used as the maximum line length instead of the first when no better break point can be found. It's ignored unless it's larger than the minimum and smaller than the maximum ๐. If you don't set the minimum explicitly, it reflects the goal algorithmically ๐งฎ.
The goal can't be smaller than the length of indentprefix plus some additional pad. Necessary adjustments happen silently, so don't be surprised ๐คซ.
๐ฆ quote-inject-head, quote-inject-tail
These are the strings that go before and after the text of a quoted message, if they're non-empty, respectively ๐ฆ. The former defaults to %f wrote:\n\n ๐. Special format directives get expanded if possible, and if so configured, the output gets folded according to quote-fold โจ.
Format specifiers in these strings start with a percent sign % and expand values from the original message, unless noted otherwise ๐. Names and addresses are not subject to the setting of showto, so keep that in mind ๐. The valid format specifiers are:
%%โ A plain percent sign ๐ .%aโ The address(es) of the sender(s) ๐ฌ.%dโ The date found in theDate:header of the message whendatefieldis set (the default). Otherwise it's the date the message was received ๐ . Formatting can be controlled by assigning a strftime(3) format string todatefield(anddatefield-markout-older) ๐ง.%fโ The full name(s), name and address as given, of the sender(s) ๐ธ.%iโ TheMessage-ID:๐.%nโ The real name(s) of the sender(s) if there's one andshownameallows usage. Otherwise, the address(es) ๐.%rโ The sender's real name(s) if there's one, otherwise the address(es) ๐ง.
๐ r-option-implicit
(Boolean) Setting this evaluates the contents of from (or, if that contains multiple addresses, sender). It then passes the results onto the used (file-based) MTA, just like the -r option does in the empty-argument case ๐.
๐ recipients-in-cc
(Boolean) When you reply, the original From: and To: get merged into the new To: by default ๐ซ . So do addressees who may have come in via Reply-To: and Mail-Followup-To: ๐ซ. If this variable is set, a sensitive algorithm tries to put only the sender of the message you're replying to in To:. Everyone else goes into Cc: ๐.
๐งพ record
Unless this variable is defined, no copies of outgoing mail get saved ๐ โโ๏ธ. If it is defined, it gives the pathname of a folder where all new, replied-to or forwarded messages get saved ๐พ. The pathname is subject to the usual โFilename transformationsโ ๐ฎ.
When saving to this folder fails, the message doesn't get sent. Instead, it's saved to DEAD ๐.
The standard says relative (fully expanded) paths are interpreted relative to the current directory (cwd) ๐บ๏ธ. To force interpretation relative to the folder outfolder, you need to set that in addition ๐ซถ.
๐ record-files
(Boolean) If this variable is set, the meaning of record gets extended to cover messages that only target file and pipe recipients (see expandaddr) ๐. These address types won't appear in recipient lists unless add-file-recipients is also set ๐.
๐ record-resent
(Boolean) If this variable is set, the meaning of record also covers the resend and Resend commands ๐ฎ.
๐ reply-in-same-charset
(Boolean) If this variable is set, S-nail first tries to use the same character set as the original message for replies ๐. If that fails, the mechanism described in โCharacter setsโ is evaluated as usual ๐.
๐ฉ๐ช reply-strings
This can be set to a comma-separated list of strings, matched case-insensitively according to ASCII rules ๐ค. They get recognised in addition to the built-in strings as Subject: reply message indicators ๐ง . The built-ins are Re:, which is mandated by RFC 5322, plus the German Aw:, Antw:, and the Wg: which has often been seen in the wild ๐ท. In other words, the separating colon has to be specified explicitly ๐
.
๐ฌ reply-to
A list of addresses to put into the Reply-To: field of the message header ๐. Members of this list are handled as if they were in the alternates list ๐.
๐ฐ๏ธ replyto
[Obsolete] This is a variant of reply-to ๐ฅ.
๐โโ๏ธ reply-to-honour
This controls whether a Reply-To: header is honoured when replying to a message via reply or Lreply ๐ซถ. It's a โquadoptionโ. If it's set without a value, it defaults to โyesโ โ
.
๐ reply-to-swap-in ๐
okay so mailing lists have been SO messy since DKIM and DMARC showed up ๐ญ A lot of them now rewrite the sender as Name via List <list@address> and stick the original sender in Reply-To:. ๐ If this is set and a Reply-To: exists and it has only one addressee (!), S-nail uses that address in place of the pretended sender ๐ฆ This works independently from reply-to-honour. The optional value is a comma-separated list of strings for finer control over when swapping happens. For now the only one supported is mlist, which swaps only when the sender is a mailing list as defined by mlist ๐
rfc822-body-from_
(Boolean) โจ Use this to force a so-called From_ line for messages embedded in an envelope mail through the message/rfc822 MIME mechanism. It's just for visual convenience ๐ธ See also mbox-rfc4155.
save
(Boolean) ๐พ Turns on saving of (partial) messages in DEAD when you hit an interrupt or a delivery error. Lifesaver energy ๐ซถ
screen
The number of lines that counts as one "screenful" ๐บ It's used for the headers summary display, searching, the message topline display, and scrolling with z. If this isn't set, S-nail works it out from the detected terminal window size and the baud rate, so the faster the terminal, the more gets shown ๐ Overall screen dimensions and pager usage are also influenced by the environment variables COLUMNS and LINES, plus the variable crt.
searchheaders
(Boolean) ๐ Expands message list specifiers written as /x:y into every message containing the substring "y" in the header field x. The search is case insensitive, so no stress ๐
sendcharsets
[Option] ๐ A comma-separated list of character set names that can be used in outgoing internet mail. The value of charset-8bit gets automatically tacked onto the end of this list. If no character set conversion capabilities are compiled into S-nail, then the only supported charset is ttycharset. Also see sendcharsets-else-ttycharset, and check out the section "Character sets" for the whole picture of character set conversion in S-nail ๐
sendcharsets-else-ttycharset
(Boolean)[Option] ๐ง If this is set but sendcharsets isn't, S-nail acts like sendcharsets was set to the value of ttycharset. Basically this passes your message data through in the character set of your current locale encoding. So mail text is (assumed to be) ISO-8859-1 when you send from an ISO-8859-1 locale, and UTF-8 when you send from a UTF-8 locale โจ
The 8-bit fallback charset-8bit never comes into play, because ttycharset is implicitly assumed to be 8-bit and able to represent all files the user may specify. (This is also the case when there's no character set conversion support in S-nail, and ttycharset is the only supported set. See "Character sets".) ๐ This might be a problem for scripts that use the suggested LC_ALL=C setting, since US-ASCII is the character set by definition in that case. So it's better to override ttycharset as well, and/or do something like this in the resource file:
# Avoid ASCII "propagates to 8-bit" when scripting
\if ! t && "$LC_ALL" != C && "$LC_CTYPE" != C
\set sendcharsets-else-ttycharset
\end
sender
An address that gets put into the Sender: field of outgoing messages ๐ฎ Per RFC 5322, this is the mailbox of the agent responsible for actually transmitting the message. It should normally not be used unless the from field has more than one address, and in that case it's required ๐ซถ > [v15 behaviour may differ] Expect automatic management of the from and sender relationship. Depending on the context, this address gets handled as if it were in the list of alternates. Also see -r and r-option-implicit ๐
sendmail
[Obsolete] ๐ฅ Predecessor of mta.
sendmail-arguments
[Obsolete] ๐ฅ Predecessor of mta-arguments.
sendmail-no-default-arguments
[Obsolete](Boolean) ๐ฅ Predecessor of mta-no-default-arguments.
sendmail-progname
[Obsolete] ๐ฅ Predecessor of mta-argv0.
sendwait
Sending to the chosen mta or to command-pipe receivers (see "On sending mail, and non-interactive mode") happens asynchronously ๐ That means you only get to see startup errors from the program, not delivery errors. Also, nobody can guarantee when the program will actually run, or when it'll have produced any output ๐ญ
If this variable is set, S-nail waits for the child program to exit and uses its exit status code to decide success ๐ฏ Remarks: this is in conflict with the POSIX standard, because sendwait is built in to be initially set ๐ Another difference is that it can have a value. That value is a comma-separated list of case-insensitive strings naming specific subsystems for which synchronousness gets ensured (and only those). The possible values are mta for mta delivery, and pcc for command-pipe receivers ๐
showlast
(Boolean) ๐ธ Makes S-nail start at the last message instead of the first one when you open a mail folder, and also with from and headers.
showname
(Boolean) ๐ง Makes S-nail show the sender's real name instead of the plain address in the header field summary and in message specifications ๐
showto
(Boolean) ๐ Shows the recipient of the message in the header summary if the message was sent by you ๐
Sign
The value backing ~A, one of the "COMMAND ESCAPES" โ๏ธ Also see message-inject-tail, on-compose-leave and on-compose-splice.
sign
The value backing ~a, one of the "COMMAND ESCAPES" โ๏ธ Also see message-inject-tail, on-compose-leave and on-compose-splice.
signature
[Obsolete] ๐ฅ Please use on-compose-splice or on-compose-splice-shell or on-compose-leave, and (if necessary) message-inject-tail instead! Bye bye, old friend ๐ญ
skipemptybody
(Boolean) ๐ If an outgoing message has an empty first or only message part, it doesn't get sent. It gets discarded instead, and that counts as success ๐ Also see the command line option -E.
smime-ca-dir, smime-ca-file
[Option] ๐ These specify where your trusted CA certificates live, in PEM (Privacy Enhanced Mail) format, so S/MIME signed messages can be verified ๐ฆ tls-ca-dir documents the prep steps you need for the former. The set of CA certificates built into the TLS library can be turned off by setting smime-ca-no-defaults, and you can fine-tune things further with smime-ca-flags โจ
smime-ca-flags
[Option] ๐ง Lets you fine-tune how the X509 CA certificate storage and the certificate verification behave. The actual values and what they mean are documented under tls-ca-flags ๐
smime-ca-no-defaults
(Boolean)[Option] ๐ซ Stops S-nail from loading the default CA locations built into the TLS library when verifying S/MIME signed messages ๐
smime-cipher-USER@HOST, smime-cipher
[Option] ๐ Picks the cipher used when generating S/MIME encrypted messages (for the specified account) ๐ RFC 5751 mandates a default of aes128 (AES-128 CBC). The possible values are case-insensitive and, in decreasing cipher strength, are:
aes256โ AES-256 CBCaes192โ AES-192 CBCaes128โ AES-128 CBCdes3โ DES EDE3 CBC, 168 bits; the default ifaes128is not availabledesโ DES CBC, 56 bits
Which cipher algorithms are actually available depends on the cryptographic library S-nail uses ๐ง [Option] Support for more cipher algorithms may be available through dynamic loading via EVP_get_cipherbyname(3) (OpenSSL), if S-nail was compiled to support it ๐ธ
smime-crl-dir
[Option] ๐ Specifies a directory containing files with CRLs in PEM format, to use when verifying S/MIME messages ๐ฆ
smime-crl-file
[Option] ๐ Specifies a file containing a CRL in PEM format, to use when verifying S/MIME messages ๐ฆ
smime-encrypt-USER@HOST
[Option] ๐ If this variable is set, messages sent to the given receiver get encrypted before sending. The value must be set to the name of a file containing a certificate in PEM format ๐
If a message goes to multiple recipients, each one that has a matching variable set will get their own individually encrypted message. Everyone else keeps receiving plain text, unless the smime-force-encryption variable is set ๐ It's recommended to sign encrypted messages too, so also set the smime-sign variable. content-description-smime-message gets inspected for messages that become encrypted ๐ซถ
smime-force-encryption
(Boolean)[Option] ๐ Makes S-nail refuse to send unencrypted messages. No plain text allowed, bestie ๐
smime-sign
This is the start of part 35, so I'll pick up right where the S/MIME options leave off.
Wait, let me reconsider. I shouldn't reintroduce the page or repeat headings from earlier parts. The original text for this part begins at smime-sign and runs through spam-interface. I'll rewrite that content in the requested voice, keeping every option, value, variable, pseudo-host, example line, and cross-reference exactly as given.
smime-sign (Boolean) [Option] ๐ ok so this one is about S/MIME signing! When it's turned on, S-nail signs your outgoing messages with the user's (from) private key and tucks the user's certificate in as a MIME attachment. โจ The payoff is that the recipient can check the sender used a valid certificate, that the email addresses in the certificate match the ones in the header, and that nobody messed with the message content ๐. It doesn't change the message text either, so people can read it like normal ๐ซถ. content-description-smime-signature will be inspected. Also see smime-sign-cert, smime-sign-include-certs and smime-sign-digest ๐ง.
smime-sign-cert-USER@HOST, smime-sign-cert [Option] Points to a file in PEM format ๐. For signing and decryption, this file has to hold the user's private key, followed by his certificate ๐.
For message signing, โUSER@HOSTโ always comes from the value of from (or, if that has multiple addresses, sender) ๐ฆ. For encryption, S-nail expects the recipient's public encryption key (certificate) ๐. The command certsave can save certificates from signed messages (check the section โSigned and encrypted messages with S/MIMEโ for the deets) ๐ . This mode is usually driven by the specialized form ๐ธ.
When decrypting, the account is figured out from the recipient fields (โTo:โ and โCc:โ). S-nail looks for addresses that have one of these variables set ๐. It always uses the first match, so if the same message went to more than one of your addresses with different encryption keys, decryption might fail ๐ญ.
Password-encrypted keys work for signing and decryption too ๐. Automated password lookup is possible through the โpseudo-hostsโ โUSER@HOST.smime-cert-keyโ for the private key, and โUSER@HOST.smime-cert-certโ for the certificate stored in the same file โจ. For example, the hypothetical address โbob@exam.pleโ could be driven with a private key / certificate pair path defined in smime-sign-cert-bob@exam.ple, and the needed passwords would then be looked up as โbob@exam.ple.smime-cert-keyโ and โbob@exam.ple.smime-cert-certโ ๐ง. When decrypting, the value of from is tried as a fallback to provide the necessary โUSER@HOSTโ ๐ซง. To include intermediate certificates, use smime-sign-include-certs ๐. The possible password sources are documented in โOn URL syntax and credential lookupโ ๐.
smime-sign-digest-USER@HOST, smime-sign-digest [Option] This picks the message digest to use when signing S/MIME messages ๐. Remember that for this, โUSER@HOSTโ refers to the variable from (or, if that has multiple addresses, sender) ๐ฉท. Which algorithms you get depends on the cryptographic library, but at least one usable built-in algorithm is guaranteed as a default ๐ท. If possible, S-nail goes against the standard RFC 5751 and uses โSHA512โ instead of the mandated โSHA1โ because of security concerns ๐ฅ. This variable gets ignored by very old (released before 2010) cryptographic libraries that don't offer the needed interface, and it'll be logged if that happens ๐ฅฒ.
S-nail will try to add built-in support for these message digests, and the names are case-insensitive ๐: โBLAKE2b512โ, โBLAKE2s256โ, โSHA3-512โ, โSHA3-384โ, โSHA3-256โ, โSHA3-224โ, plus the widely available โSHA512โ, โSHA384โ, โSHA256โ, โSHA224โ, and the proposed insecure โSHA1โ, and finally โMD5โ ๐. More digests may be available through dynamic loading via the OpenSSL function EVP_get_digestbyname(3) ๐.
smime-sign-include-certs-USER@HOST, smime-sign-include-certs [Option] If used, this should be a comma-separated list of files, and each one holds a single certificate in PEM format ๐. Those certificates get included in the S/MIME message alongside the smime-sign-cert certificate ๐. This is handy for intermediate certificates from the certificate authority, so the receiver's S/MIME software can verify the entire certificate chain ๐. That chain starts at a local root certificate, goes over the intermediate certificates, and ends at the smime-sign-cert ๐ฟ. Top level certificates can also be included in the chain, but the receiver's side won't use them for verification ๐คทโโ๏ธ.
For these mechanisms, โUSER@HOSTโ refers to the content of the internal variable from (or, if that has multiple addresses, sender) ๐ซ. The pseudo-host โUSER@HOST.smime-include-certsโ handles password lookups for these certificates, if they were given one, so the lookup can be automated through the mechanisms in โOn URL syntax and credential lookupโ ๐ค.
smime-sign-message-digest-USER@HOST, smime-sign-message-digest [Obsolete][Option] Predecessor(s) of smime-sign-digest ๐ฐ๏ธ.
smtp [Obsolete][Option] To use the built-in SMTP transport, specify an SMTP URL in mta ๐. [v15 behaviour may differ] For compatibility reasons, a set smtp is used in preference to mta ๐.
smtp-auth-USER@HOST, smtp-auth-HOST, smtp-auth [Option] This variable chain controls the SMTP mta authentication method ๐. Possible values are โnoneโ ([no v15-compat] default), โplainโ ([v15-compat] default), โloginโ, [v15-compat] โoauthbearerโ (see the โFAQโ entry โBut, how about XOAUTH2 / OAUTHBEARER?โ) and [v15-compat] โexternalโ and โexternanonโ for TLS secured connections that pass a client certificate via tls-config-pairs ๐ก๏ธ. There may also be the optional methods โcram-md5โ and โgssapiโ ๐งฉ. โnoneโ and โexternanonโ don't need any user credentials, โexternalโ and โgssapiโ need a user name, and all other methods need a user name and a password ๐ซ . โexternanonโ builds only on the credentials passed via a client certificate, and it's usually the way to go, since tested servers don't actually follow RFC 4422 aka RFC 4954 and fail if extra credentials are passed ๐. Also see mta ๐ธ. Note that smtp-auth-HOST is [v15-compat]. ([no v15-compat] Requires smtp-auth-password and smtp-auth-user. Note for smtp-auth-USER@HOST: it may be overridden depending on the sender address in the variable from ๐.)
smtp-auth-password [Option][no v15-compat] Sets the global fallback password for SMTP authentication ๐๏ธ. If the authentication method needs a password, but neither smtp-auth-password nor a matching smtp-auth-password-USER@HOST can be found, S-nail will ask for a password on the user's terminal ๐ฅ๏ธ.
smtp-auth-password-USER@HOST [no v15-compat] Overrides smtp-auth-password for specific sender addresses, depending on the variable from ๐ฏ.
smtp-auth-user [Option][no v15-compat] Sets the global fallback user name for SMTP authentication ๐งโ๐ป. If the authentication method needs a user name, but neither smtp-auth-user nor a matching smtp-auth-user-USER@HOST can be found, S-nail will ask for a user name on the user's terminal ๐ฌ.
smtp-auth-user-USER@HOST [no v15-compat] Overrides smtp-auth-user for specific values of sender addresses, depending on the variable from ๐ฆ.
smtp-hostname [Option][v15-compat] Normally S-nail uses the variable from to derive the USER@HOST information needed to issue a MAIL FROM:<> SMTP mta command ๐ฎ. Setting smtp-hostname lets it use the โUSERโ from the SMTP account (mta or the user variable chain) and the given โHOSTโ (the hostname if the empty string is given, or the local hostname as a last resort) ๐ . This often allows using an address that is itself valid but hosted by a provider other than the one from which the message is sent (in from) ๐. Setting this variable also influences generated โMessage-ID:โ and โContent-ID:โ header fields ๐. If the [Option]al IDNA support is available (see idna-disable), variable assignment is aborted when a necessary conversion fails ๐.
smtp-use-starttls-USER@HOST, smtp-use-starttls-HOST, smtp-use-starttls (Boolean)[Option] Makes S-nail issue a STARTTLS command so the SMTP mta session becomes TLS encrypted, i.e., transport layer security gets enabled ๐โจ.
socket-connect-timeout [Option] A positive number that defines how long to wait for a socket connection to be established before forcing ^ERR-TIMEDOUT โฐ.
socks-proxy-USER@HOST, socks-proxy-HOST, socks-proxy [Option] If set to the URL of a SOCKS5 server, all network activity gets proxied through it ๐งฆ. The exception is the single DNS name lookup needed to resolve the proxy URL (unnecessary when given an already resolved IP address) ๐. It's automatically squared with the environment variable SOCKS5_PROXY, so changing one adjusts the other ๐ซถ. This example creates a local SOCKS5 proxy on port 10000 that forwards to the machine โHOSTโ (with identity โUSERโ), and actual network traffic happens from there ๐ฆ:
$ ssh -D 10000 USER@HOST
$ s-nail -Ssocks-proxy=[socks5://]localhost:10000
# or =localhost:10000; no local DNS: =127.0.0.1:10000
spam-interface [Option] To use any of the spam-related commands (like spamrate), you have to define the desired spam interface by setting this variable ๐ซ. Check the manual section โHandling spamโ for the full picture of spam handling in S-nail ๐. All or none of the following interfaces may be available ๐คทโโ๏ธ:
spamcโ okay bestie, this one is all about the spamc(1) hookup ๐ It's the interaction with spamc(1) from the spamassassin(1) suite (SpamAssassin:http://spamassassin.apache.org) โจ Unlike the generic filter interface, S-nail adds the correct arguments for each command on its own and has the smarts to parse the program's output ๐ง If spamc(1) was found in PATH during compilation, a default value for spamc-command gets baked into the S-nail binary ๐ธ If you need a specific connection type (instead of putting it in a config file), you can use the variable spamc-arguments, like-d server.example.com -p 783๐ You can also set a per-user configuration with spamc-user. Heads up, though: this interface does NOT inspect theis-spamflag of a message for the command spamforget ๐filterโ generic spam filter support through freely configurable hooks ๐ง This one is made for programs like bogofilter(1), and it needs the hooks' exit status to behave, at least for the command spamrate:0means spam,1means non-spam,2means unsure, and any other return value is a hard error ๐จ Since the hooks can include shell code snippets, you can intercept the behaviour wherever you need to ๐ The hooks are spamfilter-ham, spamfilter-noham, spamfilter-nospam, spamfilter-rate and spamfilter-spam, and the manual section โHandling spamโ has examples for some programs ๐ The process environment of the hooks will have the variableMAILX_FILENAME_GENERATEDset ๐ซถ Also, spam score support for spamrate is NOT on unless the optional regular expression support is available AND the spamfilter-rate-scanscore variable is set ๐spam-maxsizeโ (Option) Messages that go over this size won't get passed through to the configured spam-interface ๐ฆ If it's unset or0, the default of420000bytes gets used ๐spamc-commandโ (Option) The path to the spamc(1) program for thespamcspam-interface ๐ฃ๏ธ The path doesn't get expanded, it's used โas isโ ๐ A fallback path gets compiled into the S-nail binary if the executable was found during compilation โจspamc-argumentsโ (Option) S-nail handles most arguments for thespamcspam-interface on its own, but sometimes you'll want to set connection-related ones yourself with this variable, like-d server.example.com -p 783๐spamc-userโ (Option) Set a username for per-user config files for thespamcspam-interface ๐ค If it's set to the empty string, S-nail uses the name of the current user ๐ฆspamfilter-ham,spamfilter-noham,spamfilter-nospam,spamfilter-rate,spamfilter-spamโ (Option) Command and argument hooks for thefilterspam-interface ๐ช The manual section โHandling spamโ has examples for some programs ๐spamfilter-rate-scanscoreโ (Option) Thefilterspam-interface is generic, so spam scores aren't supported for it by default ๐ BUT if the optional regular expression support is available, setting this variable gets around that restriction ๐ช It works like this: first a number (digits) gets parsed, and it must be followed by a semicolon;and an extended regular expression. Then that regex is used to parse the first output line of the spamfilter-rate hook, and if the evaluation works out, the group matching the number is read as a floating point scan score ๐ssl-ca-dir-USER@HOST,ssl-ca-dir-HOST,ssl-ca-dir,ssl-ca-file-USER@HOST,ssl-ca-file-HOST,ssl-ca-fileโ Obsolete, Option โ these are the old-school predecessors of tls-ca-file and tls-ca-dir ๐ต Go with the newer names ๐ssl-ca-flags-USER@HOST,ssl-ca-flags-HOST,ssl-ca-flagsโ Obsolete, Option โ old predecessor of tls-ca-flags ๐ฐ๏ธssl-ca-no-defaults-USER@HOST,ssl-ca-no-defaults-HOST,ssl-ca-no-defaultsโ Obsolete, Boolean, Option โ old predecessor of tls-ca-no-defaults ๐ฐ๏ธssl-cert-USER@HOST,ssl-cert-HOST,ssl-certโ Obsolete, Option โ please use the Certificate slot of tls-config-pairs instead ๐ssl-cipher-list-USER@HOST,ssl-cipher-list-HOST,ssl-cipher-listโ Obsolete, Option โ please use the CipherString slot of tls-config-pairs instead ๐ssl-config-fileโ Obsolete, Option โ old predecessor of tls-config-file ๐๏ธssl-config-module-USER@HOST,ssl-config-module-HOST,ssl-config-moduleโ Obsolete, Option โ old predecessor of tls-config-module ๐งฉssl-config-pairs-USER@HOST,ssl-config-pairs-HOST,ssl-config-pairsโ Obsolete, Option โ old predecessor of tls-config-pairs ๐ซงssl-crl-dir,ssl-crl-fileโ Obsolete, Option โ predecessors of tls-crl-dir and tls-crl-file ๐ฅssl-curves-USER@HOST,ssl-curves-HOST,ssl-curvesโ Obsolete, Option โ please use the Curves slot of tls-config-pairs instead ๐ssl-featuresโ Obsolete, Option, Read-only โ old predecessor of tls-features ๐ssl-key-USER@HOST,ssl-key-HOST,ssl-keyโ Obsolete, Option โ please use the PrivateKey slot of tls-config-pairs instead ๐๏ธssl-method-USER@HOST,ssl-method-HOST,ssl-methodโ Obsolete, Option โ please use the Protocol slot of tls-config-pairs instead ๐ ๏ธssl-protocol-USER@HOST,ssl-protocol-HOST,ssl-protocolโ Obsolete, Option โ please use the Protocol slot of tls-config-pairs instead ๐ ๏ธssl-rand-fileโ Obsolete, Option โ old predecessor of tls-rand-file ๐ฒssl-verify-USER@HOST,ssl-verify-HOST,ssl-verifyโ Obsolete, Option โ old predecessor of tls-verify โstealthmuaโ if you set this with NO assigned value, it stops S-nail from generating theMessage-ID:,Content-ID:andUser-Agent:header fields that have obvious references to S-nail ๐ฅท There are two pitfalls, though. First, the message id of outgoing messages isn't known anymore. Second, an expert can still use the rest of the header info to track down the originating mail user agent ๐ต๏ธโโ๏ธ If you set it to the valuenoagent, then thatMessage-ID:andContent-ID:suppression doesn't happen ๐ โโ๏ธsystem-mailrcโ (Read-only) This is the compiled-in path of the system-wide initialization file, one of the โResource filesโ: s-nail.rc ๐
โจ termcap โจ
(Option) This is a comma-separated list of Terminal Information Library (libterminfo, -lterminfo) and/or Termcap Access Library (libtermcap, -ltermcap) capabilities that get used to overwrite or define entries ๐ง See โOn terminal control and line editorโ, and escape commas with a reverse solidus \ ๐ Note that this variable only gets queried once at program startup, so it can only be specified in resource files or on the command line ๐จ It's always inspected, no matter whether features shows termcap/terminfo library support via ,+termcap, ๐
String capabilities form cap=value pairs and are expected unless noted otherwise. Numerics have to be written as cap#number, where the number is in normal decimal notation ๐ข Booleans don't have any value. They're just true or false depending on whether they're defined or not, which means S-nail does NOT support undefining an existing boolean ๐ String capability values get some expansions before they're used: notations like ^LETTER stand for control-LETTER, and \E can be used to specify escape ๐ (The control notation ^[ could lead to misreadings when a left bracket follows, which it does for the standard CSI sequence, so \E is the clearer pick ๐) Three letter octal sequences, as in \061, are supported too ๐
To specify that a terminal supports 256-colours, and to define sequences that home the cursor and produce an audible bell, one might write:
? set termcap='Co#256,home=\E[H,bel=^G'
The following terminal capabilities are or may be meaningful for the operation of the built-in line editor or S-nail in general:
am๐ โauto_right_margin, a boolean that tells us whether the right margin needs special treatment. It's related toxenl(see COLUMNS), and it only kicks in when the library backs it up โจclearorcl๐งน โclear_screen: clears the screen and sends the cursor home. (Simulated viahopluscd.)colorsorCo๐ โmax_colors, a numeric capability for the maximum number of colours. S-nail doesn't really care about the terminal beyond that, since it always emits ANSI / ISO 6429 escape sequences. Also see colour.cr๐ฆ โcarriage_return: moves to the first column of the current row. The default built-in fallback is\r.cub1orle๐ธ โcursor_left: moves the cursor left one space, non-destructively. The default built-in fallback is\b.cuf1ornd๐ โcursor_right: moves the cursor right one space, non-destructively. The default built-in fallback is\E[C, which most terminals use. Less often you'll see\ECand\EOC๐edorcd๐ซง โclr_eos: clears the screen.elorce๐งฝ โclr_eol: clears to the end of the line. (Simulated viachplus repetitions of space characters.)homeorho๐ โcursor_home: homes the cursor.hpaorch๐ฏ โcolumn_address: moves the cursor to the given column parameter in the current row. (Simulated viacrplusnd.)rmcuporte/smcuporti๐ช โexit_ca_modeandenter_ca_mode, respectively. These exit and enter the alternative screen (ca-mode), which basically turns S-nail into a fullscreen app. You have to switch this on explicitly by settingtermcap-ca-mode, so it's not sneaking up on you ๐ซถsmkxorks/rmkxorkeโจ๏ธ โkeypad_xmitandkeypad_local, respectively. They enable and disable the keypad. It's always enabled when available, because even keyboards without keypads send different key codes for things like cursor keys, and only with it on do we see the codes we actually care about.xenlorxn๐ โeat_newline_glitch: a boolean that says whether a newline written in the last column of anauto_right_marginterminal gets ignored. With it, the full terminal width is available even on autowrap terminals. This gets inspected even without,+termcap,features.
Many more capabilities that describe key-sequences are documented for bind. Go check those out too, bestie ๐
termcap-ca-mode ๐ฅ๏ธ
[Option] Lets you use the exit_ca_mode and enter_ca_mode termcapabilities to jump into an alternative exclusive screen, the so-called ca-mode. This usually needs special configuration of the PAGER, and it also depends on the value of crt. Heads up: this variable only gets queried once at program startup, so it can only be set in resource files or on the command line โ ๏ธ
termcap-disable ๐ซ
[Option] Disables any interaction with a terminal control library. If it's set, only some generic fallback built-ins, and possibly the contents of termcap, describe the terminal to S-nail. Same deal as above: it's only queried once at program startup, so it can only be set in resource files or on the command line ๐
tls-ca-dir-USER@HOST, tls-ca-dir-HOST, tls-ca-dir, tls-ca-file-USER@HOST, tls-ca-file-HOST, tls-ca-file ๐
[Option] These are the directory and the file, respectively, for pools of trusted CA certificates in PEM (Privacy Enhanced Mail) format. They're used to verify TLS server certificates. Using both at once is fine. The file gets loaded the first time it's needed, and the directory lookup gets done fresh as a last resort whenever it's necessary. The CA certificate pool built into the TLS library can be turned off via tls-ca-no-defaults, and you can fine-tune things further with tls-ca-flags. The directory search needs special filename conventions, so check out SSL_CTX_load_verify_locations(3) and verify(1) (or c_rehash(1)) ๐
tls-ca-flags-USER@HOST, tls-ca-flags-HOST, tls-ca-flags ๐๏ธ
[Option] This one lets you fine-tune how the X509 CA certificate storage and the certificate verification behave (also see tls-verify). The value is a comma-separated list of configuration directives, and any whitespace in between gets ignored. The directives map straight onto flags you can pass to X509_STORE_set_flags(3), which are usually defined in a file called openssl/x509_vfy.h. Which ones work depends on your TLS library version. A directive with no mapping just gets ignored (the error log for that is subject to debug) ๐ค
These directives are understood, and they're case-insensitive:
no-alt-chains๐ โ If the initial chain isn't trusted, don't try to build an alternative chain. Setting this makes OpenSSL verification match older OpenSSL versions, from before automatic building and checking of alternative chains existed. Also seetrusted-first.no-check-timeโฐ โ Don't check certificate or CRL validity against the current time.partial-chain๐งฉ โ By default, partial, incomplete chains that can't be verified all the way up to the chain top (a self-signed root certificate) won't verify. With this flag set, a chain succeeds if at least one signing certificate in it is in any of the configured trusted stores of CA certificates. SSL_CTX_load_verify_locations(3) has some advice on how to manage your own trusted store of CA certificates.strict๐ท โ Disables workarounds for broken certificates.trusted-first๐ฅ โ Tries building a chain using issuers from the trusted store first, to avoid problems with server-sent legacy intermediate certificates. Newer versions of OpenSSL support alternative chain checking and enable it by default, which gives the same behaviour. Also seeno-alt-chains.
tls-ca-no-defaults-USER@HOST, tls-ca-no-defaults-HOST, tls-ca-no-defaults ๐
โโ๏ธ
(Boolean)[Option] Don't load the default CA locations that are built into the TLS library, which is used to verify TLS server certificates. It's a yes-or-no switch, so keep it simple โจ
tls-config-file ๐
[Option] If this variable is set, CONF_modules_load_file(3) gets used (if it's announced via ,+modules-load-file, in tls-features) to allow resource-file-based configuration of the TLS library. This happens once the library gets used for the first time, which can even be early during startup. It gets logged with verbose, so turn that on if you're curious ๐ If a non-empty value is given, then the given file gets used instead of the TLS library's global default, after performing "Filename transformations". Be careful here: it's an error if the file can't be loaded ๐จ The application name will always be passed as s-nail. Some TLS libraries support application-specific configuration via resource files loaded like this. Please see tls-config-module for that.
tls-config-module-USER@HOST, tls-config-module-HOST, tls-config-module ๐ง
[Option] If file-based application-specific configuration via tls-config-file is available (announced as ,+ctx-config, by tls-features, which means SSL_CTX_config(3) is available), then you can use one central TLS configuration file for all programs, including s-nail. For example:
# Register a configuration section for s-nail
s-nail = mailx_master
# The top configuration section creates a relation
# in between dynamic SSL configuration and an actual
# program specific configuration section
[mailx_master]
ssl_conf = mailx_tls_config
# And that program specific configuration section now
# can map diverse tls-config-module names to sections,
# as in: tls-config-module=account_xy
[mailx_tls_config]
account_xy = mailx_account_xy
account_yz = mailx_account_yz
[mailx_account_xy]
MinProtocol = TLSv1.2
Curves=P-521
[mailx_account_yz]
CipherString = TLSv1.2:!aNULL:!eNULL:
MinProtocol = TLSv1.1
Options = Bugs
๐ tls-config-pairs-USER@HOST, tls-config-pairs-HOST, tls-config-pairs ๐
[Option] okay so this variable chain gets read as a comma-separated list of directive/value pairs ๐ Each directive and its value are joined by an equals sign =, and any whitespace around the pair members gets trimmed off โ๏ธ Keys are (usually) case-insensitive ๐ธ
Here's the tea ๐ซ when you put these pairs in a tls-config-module section of a tls-config-file, commas , need to be escaped with a reverse solidus \ ๐ But when you're doing it here, it's different: if the equals sign = is preceded by an asterisk *, then "Filename transformations" get performed on the value, and if those fail it's an error ๐ญ Also, unless tls-features announces proper support (,+conf-ctx,), only the keys listed below are supported. Otherwise the pairs get passed straight through as arguments to the function SSL_CONF_cmd(3) โจ
Certificateโ the filename of a TLS client certificate (chain) that some servers need ๐ If it doesn't work, it falls back toSSL_CTX_use_certificate_chain_file(3). "Filename transformations" happen here too. IfPrivateKeyisn't set explicitly, it copies this same value ๐ Some services also do so-calledexternalauthentication once a TLS client certificate got presented successfully during connection setup. (Connecting is authenticating, bestie ๐ค๐ฅ)CipherStringโ a list of ciphers for TLS connections, seeciphers(1)๐ By default nothing gets set, so you get a Protocol-specific list of ciphers instead. The protocol standards define which ciphers are acceptable, possibly cramped by whatever TLS library is in use ๐ฅฒ The fallback isSSL_CTX_set_cipher_list(3).Ciphersuitesโ a list of ciphers for TLSv1.3 connections, seeciphers(1)๐ฆ These get joined onto the list fromCipherString. It's only available iftls-featuresannounces,+ctx-set-ciphersuites,, and it's applied as necessary viaSSL_CTX_set_ciphersuites(3).Curvesโ a list of supported elliptic curves, if applicable ๐ By default no curves are set. The fallback isSSL_CTX_set1_curves_list(3), if available.MaxProtocol,MinProtocolโ the maximum and minimum supported TLS versions, respectively ๐ถ These are only available iftls-featuresannounces,+ctx-set-maxmin-proto,, and they're applied as necessary viaSSL_CTX_set_max_proto_version(3)andSSL_CTX_set_min_proto_version(3). These fallbacks use an internal parser that understands the stringsSSLv3,TLSv1,TLSv1.1,TLSv1.2,TLSv1.3, and the special valueNone, which disables the given limit ๐Optionsโ various flags to set ๐๏ธ The fallback isSSL_CTX_set_options(3), and in that case any value other than (exactly)Bugsresults in an error ๐PrivateKeyโ the filename of the private key, in PEM format, of a TLS client certificate ๐๏ธ If it's unset, the value ofCertificategets used instead. "Filename transformations" are performed here too. The fallback isSSL_CTX_use_PrivateKey_file(3).Protocolโ the TLS protocol that gets used ๐ง Iftls-featuresannounces,+conf-ctx,orctx-set-maxmin-proto, then usingMaxProtocolandMinProtocolis preferable โจ The fallback isSSL_CTX_set_options(3), driven by an internal parser that understands the stringsSSLv3,TLSv1,TLSv1.1,TLSv1.2,TLSv1.3, and the special valueALL. You can give multiple protocols as a comma-separated list, and any whitespace gets ignored. An optional plus sign+prefix enables a protocol, while a hyphen-minus-prefix disables it. So-ALL, TLSv1.2enables only the TLSv1.2 protocol ๐
๐ tls-crl-dir, tls-crl-file ๐
[Option] these specify a directory (for tls-crl-dir) or a file (for tls-crl-file) respectively, containing a CRL in PEM format ๐ It gets used when verifying TLS server certificates ๐
๐งฉ tls-features ๐งฉ
[Option] (Read-only) this expands to a comma-separated list of the TLS library identity and any optional features ๐ซง To make substring matching easier, the string always starts and ends with a comma. The currently supported identities are libressl (LibreSSL), libssl-0x30000 (OpenSSL v3.0.0 series), libssl-0x10100 (OpenSSL v1.1.x series), and libssl-0x10000 (elder OpenSSL series, other clones) ๐ฐ๏ธ Optional features get a plus sign + in front when they're available, and a hyphen-minus - otherwise โจ
The currently known features are:
conf-ctx(tls-config-pairs) ๐ctx-config(tls-config-module) ๐ธctx-set-ciphersuites(Ciphersuites slot oftls-config-pairs) ๐ฆctx-set-maxmin-proto(tls-config-pairs) ๐ถmodules-load-file(tls-config-file) ๐tls-rand-file(tls-rand-file) ๐ฒ
๐ tls-fingerprint-USER@HOST, tls-fingerprint-HOST, tls-fingerprint ๐
[Option] it's possible to replace the verification of the connection peer certificate against the entire local pool of CAs (for more, see "Encrypted network communication") with a comparison against a precalculated certificate message digest ๐ซถ That's the so-called fingerprint, and it gets specified as the tls-fingerprint-digest you're using. You can calculate this fingerprint with tls fingerprint HOST ๐ง
๐งฎ tls-fingerprint-digest-USER@HOST, tls-fingerprint-digest-HOST, tls-fingerprint-digest ๐งฎ
[Option] this is the message digest to use when creating TLS certificate fingerprints ๐ข The defaults, if available, get tried in this order: BLAKE2s256, then SHA256 ๐
For the complete list of digest algorithms, refer to smime-sign-digest ๐
๐ฒ tls-rand-file ๐ฒ
[Option] if tls-features announces ,+tls-rand-file,, then this variable gets queried to find a file with random entropy data ๐ That data can be used to seed the P(seudo)R(andom)N(umber)G(enerator), see RAND_load_file(3). If this variable is not set or is empty, or if the "Filename transformations" fail, the default filename gets used instead. That default is RAND_file_name(3), which is normally ~/.rnd ๐ If seeding the PRNG was successful, RAND_write_file(3) gets called to update the entropy ๐ซ
Remarks: libraries that don't announce this feature seed the PRNG by other means, so this one's not for them ๐
๐ tls-verify-USER@HOST, tls-verify-HOST, tls-verify ๐
[Option] this variable chain sets the action to perform if an error occurs during TLS server certificate validation ๐จ Validation happens against the specified or default trust stores (tls-ca-dir, tls-ca-file, or the TLS library's built-in defaults, unless usage is disallowed via tls-ca-no-defaults), and it's fine-tuned via tls-ca-flags. The valid (case-insensitive) values are:
strictโ fail and close the connection immediately ๐ซaskโ ask whether to continue, on standard input ๐คwarnโ show a warning and continue โ ๏ธignoreโ do not perform validation ๐
The default is ask ๐
๐ toplines ๐
If defined, this gives the number of lines of a message to display with the command top ๐จ If it's unset, the first five lines get printed. If it's set to 0, the variable screen gets inspected instead. If the value is negative, then its absolute value gets used for unsigned right shifting (see vexpr) the screen height ๐งฎ
๐ซง topsqueeze ๐ซง
(Boolean) if set, the top command series strips adjacent empty lines and quotations ๐งน
๐ค ttycharset ๐ค
The character set of the terminal S-nail operates on ๐ฌ It's also the one and only supported character set that S-nail can use if no character set conversion capabilities were compiled into it. In that case it defaults to ISO-8859-1. Otherwise it defaults to UTF-8 ๐ท With sufficient locale support, the default gets deduced from the locale environment if that's set (for example LC_CTYPE, see there for more) ๐บ๏ธ Runtime locale changes get reflected by ttycharset, except during the program startup phase, and except when -S was used to freeze the given value ๐ง Refer to the section "Character sets" for the complete picture about character sets ๐
๐ typescript-mode ๐
(Boolean) a special multiplex variable that disables all variables and settings which would interfere with running S-nail in script(1) ๐งพ It sets colour-disable, line-editor-disable, and (before startup has completed only) termcap-disable ๐
Unsetting it does not restore the former state of the covered settings, so be careful, bestie ๐ฌ
๐ก๏ธ umask ๐ก๏ธ
For a safe-by-default policy, the process file mode creation mask umask(2) gets set to 0077 on program startup, after the resource files have been loaded, and unless this variable is set ๐ Assigning this an empty value leaves the active setting unchanged. Otherwise, the given value becomes the new file mode creation mask ๐ Child processes inherit the file mode creation mask of their parent ๐
๐ค user-HOST, user ๐ค
[v15-compat] this variable chain sets a global fallback user name ๐ซถ It's used when no user name was given in the protocol and account-specific URL. This variable defaults to the name of the user who runs S-nail ๐
v15-compat๐ this one is all about staying upward compatible with S-nail version 15.0, so it decides which config options exist and how they get handled ๐. If you set it to any non-empty value, the command modifierwyshgets implied, which means you get Shell-style argument quoting instead of Old-style argument quoting for every command that supports both โจ. This manual uses[v15-compat]and[no v15-compat]to mean the new way and the old way of doing things, respectively ๐ฆverbose๐ง verbose mode turns on logging of informational context messages. It used to be a Boolean, but now it can be set multiple times (that's what the command line option-vdoes), or you can give it a numeric value to crank up the verbosity ๐ฅ. Setting it to0switches verbosity off and basically counts as unset (almost, ngl ๐). The maximum number is3. Also check outdebug๐version,version-date,version-hexnum,version-major,version-minor,version-update๐ these are all read-only, and they're the S-nail version info squad ๐.versionis a string with the complete version identification.version-dateis the release date in ISO 8601 notation, without the time.version-hexnumis a 32-bit hexadecimal number where the upper 8 bits store the major version, followed by the minor and update version numbers, which each take up 12 bits ๐. The last three variables,version-major,version-minorandversion-update, only contain decimal digits: the major, minor and update version numbers. The output of the commandversionwill include all of this ๐writebackedited๐ if this variable is set, messages you modified with theeditorvisualcommands get written back to the current folder when you quit it. Heads up though, it's only honoured for writable folders in MBOX format ๐จ. Also, the editor gets pointed at the raw message content in that case, so neither MIME decoding nor decryption has been performed ๐ญ. And proper mbox-rfc4155 โFrom_โ quoting of newly added or edited content is also left as an exercise for the user ๐
โจ ENVIRONMENT โจ
okay bestie, let's talk about environment variables ๐ธ the term is basically a vibe check: these are either officially standardized as main-character parts of the process environment, or they're just commonly found hanging out in there ๐
once S-nail starts, it inherits the environment from sh(1). unless I explicitly say otherwise, these variables blend right into the "INTERNAL VARIABLES" world from S-nail's point of view โจ so you can manage them with set and unset, and S-nail updates the program environment automatically, which newly created child processes then inherit ๐ฆ
if you want other environment variables to be just as integrated, you need to import (link) them with the command environ ๐ง it can also set and unset non-integrated variables from scratch, as long as your system supports it ๐ here's a POSIX shell example where COLUMNS is set for S-nail only, and EDITOR gets exported first so it affects everything else the running shell does ๐
$ EDITOR="vim -u ${HOME}/.vimrc"
$ export EDITOR
$ COLUMNS=80 s-nail -R
now the list of the girlies:
COLUMNSโ the user's preferred width in column positions for the terminal screen ๐ it gets checked and used once at startup in interactive or batch (-#) mode, and after that it's actively managed for child processes and the MLE (see "On terminal control and line editor") in interactive mode ๐ non-interactive mode always uses a compile-time constant as its fallback, which is 80 columns by default. if you're in batch mode andCOLUMNSandLINESare both set but not both usable (empty, not a number, or 0) at startup, S-nail will (try to) figure out the real terminal screen size once ๐ (normallysh(1)handles these and unsets them for pipe specifications etc.)DEADโ the name of the (mailbox) folder where aborted messages get saved ifsaveis set ๐ it defaults to~/dead.letter. ifdebugis set, no output gets generated; otherwise the file's contents get replaced ๐ except for shell globs, "Filename transformations" (also seefolder) get performed here too โจEDITORโ the pathname of the text editor for theeditcommand and~e(see "COMMAND ESCAPES") โ๏ธVISUALis for the more display-oriented editor ๐ฆHOMEโ the user's home directory ๐ก this one only gets used when it actually lives in the process environment ๐ if this directory doesn't exist, isn't accessible, or can't be read, S-nail uses the calling user's home directory instead, and it always uses that for the root user ๐ (there's no writability test, so non-privileged users inside read-only jails can still use it, but depending on settings this directory is a default write target for things likeDEAD,MBOXand more ๐ซถ)LC_ALL,LC_CTYPE,LANGโ [Option] the names of the locale(7) (checked in this lookup order, and/or seesetlocale(3)) that decide which "Character sets" are used ๐ changing these at runtime triggers automatic updates of the whole locale system, which includes updatingttycharset(except during startup, when the variable has been frozen via-S) โจLINESโ the user's preferred number of lines for the terminal screen ๐ฑ it behaves likeCOLUMNS, except the compile-time constant used in non-interactive mode and as a fallback defaults to 24 (lines) ๐LISTERโ the pathname of the directory lister used by thefolderscommand when working with local mailboxes ๐ the default isls(1)(path search throughSHELL) ๐LOGNAMEโ at startup S-nail actively makes sure this refers to the name of the user running S-nail, so it can pass a verified name to any newly created child process ๐ซถMAILโ used as the user's "primary system mailbox" unlessinboxis set ๐ฌ if the environmental fallback isn't set either, a built-in compile-time default gets used ๐ it's assumed to be an absolute pathname ๐MAILCAPSโ [Option] overrides the default path search for "The Mailcap files" ๐๏ธ every existing file found gets loaded in sequence, with its contents appended to the list of MIME type handler directives ๐ธ otherwise, the RFC 1524 standard default gets assigned:~/.mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcapโจ (the default value is a compile-time [Option].)MAILRCโ used as the startup file instead of~/.mailrcif it's set ๐ to avoid side effects from configuration files, scripts should either set this to/dev/nullor use the-:command line option ๐MAILX_NO_SYSTEM_RCโ if this is set, readings-nail.rc(aka system-mailrc) at startup gets blocked ๐ซ the same effect happens as if S-nail was started with the option-:(and its argument) or-n๐ this variable is only used when it lives in the process environment ๐ซถMBOXโ the name of the user's "secondary mailbox" file ๐ฆ a logical subset of the special "Filename transformations" (also seefolder) is supported ๐ฆ the default is~/mbox๐ traditionally,MBOXis the file where read messages from the "primary system mailbox" get saved ๐ also see "Message states" โจNETRCโ [v15-compat][Option] overrides the default location of the user's~/.netrcfile ๐PAGERโ the pathname of the program used for backing themorecommand, and for when thecrtvariable forces a pager for output ๐ the default paginator ismore(1)(path search throughSHELL) ๐
S-nail checks what's in this variable: if it contains the string โlessโ, then a non-existing LESS environment variable gets set to (the portable) RI ๐ง likewise, for โlvโ, LV will optionally be set to -c ๐
also see colour-pager ๐จ
PATHโ a colon-separated list of directories the shell searches when looking for commands, for example/bin:/usr/bin:/usr/local/bin๐บ๏ธPOSIXLY_CORRECTโ this environment entry gets automatically squared with posix ๐ฏSHELLโ the shell used for the commands!,shell,~!(see "COMMAND ESCAPES"), and when starting subprocesses ๐ if this isn't defined, a default shell gets used ๐ซถSOCKS5_PROXYโ this environment entry gets automatically squared with socks-proxy ๐งฆSOURCE_DATE_EPOCHโ a time in seconds since the Unix epoch (1970-01-01) that gets used in place of the current time โฐ it's looked up at startup, and its mere existence switches S-nail into a reproducible mode (https://reproducible-builds.org) ๐ that mode uses deterministic random numbers, a special fixated pseudoLOGNAMEand more ๐งฉ this operation mode is used for development and by software packagers ๐ฆ [v15 behaviour may differ] right now, an invalid setting is only ignored, rather than causing the program to abort ๐
$ SOURCE_DATE_EPOCH=`date +%s` s-nail
TERMโ [Option] the terminal type that output is prepared for ๐ฅ๏ธ for extended colour and font control, check out "Coloured display", and for terminal management in general, check out "On terminal control and line editor" ๐TMPDIRโ except for the root user, this variable defines the directory for temporary files, used instead of/tmp(or the given compile-time constant) if it's set, existent, accessible, and both readable and writable ๐๏ธ it's only used when it lives in the process environment, but S-nail makes sure at startup that this variable gets updated to contain a usable temporary directory ๐ซถUSERโ identical toLOGNAME(see there), but it's not standardized, so you shouldn't use it ๐ โโ๏ธ it only gets corrected if it's already set ๐VISUALโ the pathname of the text editor for thevisualcommand and~v(see "COMMAND ESCAPES") ๐ผ๏ธEDITORis used for the less display-oriented editor ๐ฆ
โจ FILES โจ
~/.mailcap,/etc/mailcapโ [Option] personal and system-wide MIME type handler definition files ๐ see "The Mailcap files" ๐ (the shown names are part of the RFC 1524 standard search pathMAILCAPS.)~/.mailrc,s-nail.rcโ user-specific and system-wide files giving initial commands, the "Resource files" ๐ (the filenames used come fromMAILRCand system-mailrc, respectively.)~/mboxโ the default value forMBOX๐ฆ~/.mime.types,/etc/mime.typesโ personal and system-wide MIME types ๐๏ธ see "The mime.types files" ๐ธ~/.netrcโ [v15-compat][Option] the default location of the user's.netrcfile ๐ the section "The .netrc file" documents the file format โจ the path can be set viaNETRC๐/dev/nullโ the data sinknull(4)๐ณ๏ธ~/.rndโ [Option] a possible location for persistent random entropy seed storage, seetls-rand-file๐ฒ
โจ Resource files โจ
Upon startup, S-nail reads in several resource files, in this order ๐
s-nail.rcโ the system-wide initialization file (system-mailrc) ๐ reading this file can be suppressed, either with the-:(and its argument) or-ncommand line options, or by setting the "ENVIRONMENT" variableMAILX_NO_SYSTEM_RC๐ซ~/.mailrcโ the file giving initial commands ๐ a different file can be chosen by setting the "ENVIRONMENT" variableMAILRC๐ reading this file can be suppressed with the-:command line option ๐mailx-extra-rcโ defines a startup file that gets read after all the other resource files ๐ it can be used to specify settings that aren't understood by other mailx(1) implementations, for example ๐งฉ
The content of these files is interpreted as follows:
- Okay bestie, first up: the whitespace characters space, tabulator, and newline, plus whatever's listed in
ifs, get stripped off the start and end of input lines. ๐งนโจ - Empty lines? Ignored. Totally invisible to S-nail. ๐ซง
- Anything else is a command ๐
. It can stretch across multiple lines if you end a line with a reverse solidus
\. The leading whitespace on the follow-up lines gets ignored, but trailing whitespace right before the escaped newline stays in the input. ๐ฆ - If the line content starts with a number sign
#, it's a comment-command ๐. It's a real command that does absolutely nothing, so the usual follow-line mechanism still applies! Iykyk. ๐
Errors while loading these files are subject to the settings of errexit and posix. โ ๏ธ You can source more files with syntactically equal content too. Here's an example of what a saved file could look like ๐ธ:
# This line is a comment command. And y\
es, it is really continued here.
set debug \
verbose
set editheaders
๐ The mime.types files ๐
As stated in โHTML mail and MIME attachmentsโ, S-nail needs to learn about MIME (Multipurpose Internet Mail Extensions) media types so it can classify message and attachment content. ๐ One source for these is mime.types files, and you can control whether they load by setting the variable mimetypes-load-control. Another source is the command mimetype, which also gives you access to S-nail's MIME type cache. mime.types files have this syntax:
type/subtype extension [extension ...]
# For example text/html html htm
Here type/subtype defines the MIME media type, as standardized in RFC 2046. โจ The type declares the general kind of data, and the subtype gets specific about the format for that kind of data. One or more filename extensions, separated by whitespace, can be bound to the media type format. Comments can show up anywhere on a line after a number sign #, and that makes the rest of the line get discarded. ๐๏ธ
S-nail also supports an extended, non-portable syntax in especially crafted files ๐ฅ. You load those through the alternative value syntax of mimetypes-load-control, and they can start with an optional type-marker:
[type-marker ]type/subtype extension [extension ...]
These type markers are supported:
?โ Treat message parts with this content as plain text. ๐?tโ The same as plain?. ๐?hโ Treat message parts with this content as HTML tagsoup. If the [Option]al HTML-tagsoup-to-text converter isn't available, the content gets treated as plain text instead. ๐?Hโ Likewise?h, but instead of falling back to plain text, it requires an explicit content handler to be defined. ๐?qโ If no handler can be found, a text message gets displayed that says so. Honestly kind of annoying ๐, because signatures serve a contextual purpose and their content is useless by itself. This marker skips displaying that text message. ๐
Further reading, bestie ๐: for sending messages, check out mimetype, mime-allow-text-controls, and mimetypes-load-control. For reading and so on, check out โHTML mail and MIME attachmentsโ, โThe Mailcap filesโ, mimetype, mime-counter-evidence, mimetypes-load-control, pipe-TYPE/SUBTYPE, and pipe-EXTENSION. ๐ฆ
๐ง The Mailcap files ๐ง
[Option] RFC 1524 defines a โUser Agent Configuration Mechanismโ ๐ธ. It's how mail user agent programs learn about the locally installed facilities for handling different data formats, aka which commands can display, edit, et cetera MIME part contents, and how to use them. It also sets up a default path search that checks multiple possible locations for resource files, and there's the MAILCAPS environment variable if you want to overwrite that. Handlers found during the path search get cached. The command mailcap operates on that cache, and the variable mailcap-disable stops automatic loading and any use of mailcap handlers. โHTML mail and MIME attachmentsโ gives you the general overview of how MIME types get handled. ๐ซถ
โMailcapโ files are a set of newline separated entries. Comment lines start with a number sign # (in the first column!!) and get ignored ๐. Empty lines get ignored too. Every other line is read as a mailcap entry. An entry can be split over multiple lines by putting the reverse solidus character \ at the end of all but the final line. The standard doesn't say how leading whitespace on successive lines should be treated, so it gets kept. ๐ง
โMailcapโ entries are made of a bunch of semicolon ; separated fields. The first two fields are mandatory and have to show up in that order ๐. The rest are optional and can appear in any order. Leading and trailing whitespace of field content gets ignored (removed). The reverse solidus \ can escape any following character, including the semicolon and itself, in the content of the second field, and in the value parts of any optional key/value field. ๐
The first field defines the MIME TYPE/SUBTYPE the entry is about to handle, and it's case-insensitive. If the subtype is an asterisk *, the entry matches all subtypes of the named type. For example, audio/* would match any audio type. The second field is the view shell command used to display MIME parts of the given type. ๐ง
Data-consuming shell commands get fed message (MIME part) data on standard input, unless one or more instances of the (unquoted) string %s are used. In that case, %s gets replaced with a temporary file(name) that's been prefilled with the part's data. Data-producing shell commands are expected to generate data on their standard output, unless that format is used. In all cases, any given %s format gets replaced with a properly shell quoted filename. When a command requests a temporary file via %s, that file gets removed again, as if the x-mailx-tmpfile and x-mailx-tmpfile-fill flags had been set. Unless the command requests x-mailx-async, the x-mailx-tmpfile-unlink flag is also implied. See below for more ๐.
Optional fields define single-word flags (case-insensitive), or key / value pairs. Those pairs are made of a case-insensitive keyword, an equals sign =, and a shell command. Whitespace around the equals sign gets removed. The optional fields include the following:
composeโ A program that can compose a new body or body part in the given format. (Currently unused.) ๐คcomposetypedโ Similar to the compose field, but used when the composing program needs to specify theContent-type:header field to be applied to the composed data. (Currently unused.) ๐คcopiousoutputโ A flag field saying the output of the view command can be integrated into S-nail's normal visual display. It's mutually exclusive withneedsterminal. ๐descriptionโ A textual description of this type of data. The text can optionally be wrapped in double quotation marks". ๐editโ A program that can edit a body or body part in the given format. (Currently unused.) ๐คnametemplateโ This field sets a filename format for the%sformat used in the shell command fields. The%sgets replaced by a random string. (The filename is also stored in and passed to subprocesses viaMAILX_FILENAME_TEMPORARY.) The standard says this is โonly expected to be relevant in environments where filename extensions are meaningfulโ, so this field gets ignored unless the%sis a prefix, optionally followed by (ASCII) alphabetic and numeric characters, the underscore and the period. For example, to pass a JPG file to an image viewer with a name ending in.jpg, you can usenametemplate=%s.jpg๐ผ๏ธ.needsterminalโ This flag field means the given shell command has to run on an interactive terminal. In interactive mode, S-nail temporarily hands the terminal over to that command. In non-interactive mode, this entry gets entirely ignored. This flag impliesx-mailx-noquote. ๐ฅ๏ธprintโ A program that can print a message or body part in the given format. (Currently unused.) ๐คtestโ Specifies a program that runs to test some condition, like the machine architecture or the window system in use, to decide whether this mailcap entry applies. If the test fails, a subsequent mailcap entry should be sought. Also seex-mailx-test-once. Standard I/O of the test program is redirected from and to/dev/null, and the format%sisn't supported, since the data doesn't exist yet. ๐งชtextualnewlinesโ A flag field saying this type of data is line-oriented. If it's encoded inbase64, all newlines get converted to canonical form (CRLF) before encoding, and they stay in that form after decoding. (Currently unused.) ๐คx11-bitmapโ Names a file, in X11 bitmap (xbm) format, that points to an appropriate icon to visually denote this kind of data. This field is not used by S-nail. ๐ซx-mailx-asyncโ An extension flag field meaning the given view command runs asynchronously, without blocking S-nail โก. It can't be used together withneedsterminal, and the command's standard output goes to/dev/null.x-mailx-noquoteโ An extension flag field saying that even acopiousoutputview command must not be used when quoting messages, which it would do by default. ๐ โโ๏ธx-mailx-test-onceโ An extension flag that decides whether the given test command gets evaluated just once, with its exit status cached. This is handy for global, unchanging conditions, like โrunning under the X Window Systemโ ๐ช.x-mailx-tmpfileโ ๐ฆ This one's an extension flag that asks for a brand-new, zero-sized temporary file, and its name gets stashed in theMAILX_FILENAME_TEMPORARYenvironment variable. โ ๏ธ Don't pair it with a command that has a%sformat, because that format is built on this temp file and it'll throw an error ๐ญ.x-mailx-tmpfile-fillโ ๐ธ Usually the MIME part's content gets fed to the handler through standard input. Set this flag and the data gets written into the impliedx-mailx-tmpfileinstead. โจ To actually get that temp file deleted, you have to setx-mailx-tmpfile-unlinkyourself, it won't happen automatically! It's also an error to combine this with a%sformat.x-mailx-tmpfile-unlinkโ ๐งน An extension flag that tells S-nail to delete the temporary file automatically once the command loop is entered again, at the latest. โ Using it with a%sformat is an error, and so is combining it withx-mailx-async.x-mailx-tmpfileis implied, so you don't need to add it separately ๐ .x-mailx-last-resortโ ๐ฅ An extension flag saying this handler is only for emergencies. It gets used only when no other source (see "HTML mail and MIME attachments") can provide a MIME handler.x-mailx-ignoreโ ๐ซ An extension that makes sure this handler never gets used, period. No exceptions, bestie.
The standard lets you define as many extra fields as you want, as long as they start with x-. โจ Flag fields apply to the whole "Mailcap" entry. In some rare situations that might not be what you want, but you can fix it by making separate entries, since later entries get searched whenever an earlier one doesn't give enough info. ๐ง For instance, if a view command needs needsterminal but the compose command doesn't, this setup takes care of the compose side:
application/postscript; ps-to-terminal %s; needsterminal
application/postscript; ps-to-terminal %s; compose=idraw %s
Inside the value parts of command fields, the format string %t gets swapped out for the TYPE/SUBTYPE spec ๐. You can also drop any named parameter from a message's Content-type: field into your command line by writing %{, then the parameter name, then a closing }. Each parameter should show up as one single command line argument, even if it has spaces in it, because the RFC 1524 processor handles shell quoting for you. Like this:
# Message
Content-type: multipart/mixed; boundary=42
# Mailcap file
multipart/*; /usr/local/bin/showmulti \
%t %{boundary} ; composetyped = /usr/local/bin/makemulti
# Executed shell command
/usr/local/bin/showmulti multipart/mixed 42
๐ โโ๏ธ Heads up, S-nail does not support handlers for multipart MIME parts the way that example shows, at least as of today. It also doesn't support the extra formats%nand%F.
Here's an example file that also shows how to handle %s properly. The expansion of %s already includes any quotes it needs to be a valid shell argument by itself, so putting extra user-provided quotes around it will cause unwanted behaviour ๐.
# Comment line
text/richtext; richtext %s; copiousoutput
text/x-perl; perl -cWT %s; nametemplate = %s.pl
# Exit EX_TEMPFAIL=75 on signal
application/pdf; \
infile=%s\; \
trap "rm -f ${infile}" EXIT\; \
trap "exit 75" INT QUIT TERM\; \
mupdf "${infile}"; \
test = [ -n "${DISPLAY}" ]; \
nametemplate = %s.pdf; x-mailx-async
application/pdf; pdftotext -layout - -; copiousoutput
application/*; echo "This is \\"%t\\" but \
is 50 \% Greek to me" \; < %s head -c 512 | cat -vet; \
copiousoutput; x-mailx-noquote; x-mailx-last-resort
๐ Further reading: "HTML mail and MIME attachments", "The mime.types files", mimetype, MAILCAPS, mime-counter-evidence, pipe-TYPE/SUBTYPE, pipe-EXTENSION.
๐ The .netrc file ๐
User credentials for machine accounts (see "On URL syntax and credential lookup") can live in the .netrc file. It gets loaded and cached when netrc-lookup asks for it. The default location ~/.netrc can be swapped out with the NETRC environment variable ๐๏ธ. As long as the syntax rules are followed, the file source can be replaced with the output of the shell command set in netrc-pipe, which is handy if you want to load an encrypted file, for example. The cache can be managed with the netrc command.
The file is made of tokens separated by spaces, tabs or newlines. This parser covers a superset of the original BSD syntax, but if your .netrc needs to work across multiple programs and platforms, keep these portability glitches in mind ๐:
- BSD only supports double quotation marks, like
password "pass with spaces". - BSD (only?) supports escaping single characters with a reverse solidus (so a space could be escaped as
\), both inside and outside of a quoted string. This method is assumed to be present, and it's actively used to quote double quotation marks"and reverse solidus\characters inside the login and password tokens, for example for display purposes. - BSD doesn't require a closing quotation mark on the last user input token.
- The original BSD (Berknet) parser also supported a format that let tokens be separated with commas. At least Hewlett-Packard still seems to support that syntax, but this parser does not ๐ โโ๏ธ!
- As a non-portable extension that some widely-used programs support, shell-style comments work like this: if an input line starts with a number sign
#(after any amount of whitespace), the rest of the line gets ignored ๐ฌ. - Some other programs may require the
.netrcfile to be accessible only by the user if it holds a password token for any login other than "anonymous". This parser always requires these strict permissions ๐.
Of the supported tokens listed below, this parser only uses (and caches) machine, login and password. An existing default entry will not be used โจ.
machine nameโ ๐ฅ๏ธ The hostname of the entry's machine, lowercase-normalized before use. Any further file content, until end-of-file or the nextmachineordefaultfirst-class token, is bound (only related) to that machine name.
As an extension that shouldn't cause any worries, this parser supports a single wildcard prefix for the name:
machine *.example.com login USER password PASS
machine pop3.example.com login USER password PASS
machine smtp.example.com login USER password PASS
This would match xy.example.com as well as pop3.example.com, but neither example.com nor local.smtp.example.com. In the example, neither pop3.example.com nor smtp.example.com gets matched by the wildcard, since exact matches take precedence (although it's faster to specify things the other way around) ๐โโ๏ธ.
defaultโ ๐ This is the same asmachine, except it's a fallback entry that gets used only if none of the specified machines match. Only onedefaulttoken may be specified, and it must be the last first-class token.login nameโ ๐โโ๏ธ The user name on the remote machine.password stringโ ๐คซ The user's password on the remote machine.account stringโ ๐ณ Supplies an additional account password. This is merely for FTP purposes.macdef nameโ ๐ช Defines a macro with the specified name. It's formed from all lines beginning with the next line and continuing until a blank line is encountered (consecutive newline characters count as that blank line). โ ๏ธ Note thatmacdefentries can't be shared by multiple machines, so they must be defined after the machine they're meant for. If a macro namedinitexists, it gets automatically run as the last step of the login process. This is merely for FTP purposes.
โจ EXAMPLES โจ
An example configuration
# This example assumes v15.0 compatibility mode
set v15-compat
# Request strict TLL transport layer security checks
set tls-verify=strict
# Where are the up-to-date TLS certificates?
# (Since we manage up-to-date ones explicitly, do not use any,
# possibly outdated, default certificates shipped with OpenSSL)
#set tls-ca-dir=/etc/ssl/certs
set tls-ca-file=/etc/ssl/certs/ca-certificates.crt
set tls-ca-no-defaults
#set tls-ca-flags=partial-chain
wysh set smime-ca-file="${tls-ca-file}" \
smime-ca-no-defaults #smime-ca-flags="${tls-ca-flags}"
# This could be outsourced to a central configuration file via
# tls-config-file plus tls-config-module if the used library allows.
# CipherString: explicitly define the list of ciphers, which may
# improve security, especially with protocols older than TLS v1.2.
# See ciphers(1). Possibly best to only use tls-config-pairs-HOST
# (or -USER@HOST), as necessary, again..
# Note that TLSv1.3 uses Ciphersuites= instead, which will join
# with CipherString (if protocols older than v1.3 are allowed)
# Curves: especially with TLSv1.3 curves selection may be desired.
# MinProtocol,MaxProtocol: do not use protocols older than TLS v1.2.
# Change this only when the remote server does not support it:
# maybe use chain support via tls-config-pairs-HOST / -USER@HOST
# to define such explicit exceptions, then, e.g.,
# MinProtocol=TLSv1.1
if "$tls-features" =% ,+ctx-set-maxmin-proto,
wysh set tls-config-pairs='\
CipherString=TLSv1.2:!aNULL:!eNULL:@STRENGTH,\
Curves=P-521:P-384:P-256,\
MinProtocol=TLSv1.1'
else
wysh set tls-config-pairs='\
CipherString=TLSv1.2:!aNULL:!eNULL:@STRENGTH,\
Curves=P-521:P-384:P-256,\
Protocol=-ALL\,+TLSv1.1 \, +TLSv1.2\, +TLSv1.3'
endif
# Essential setting: select allowed character sets
set sendcharsets=utf-8,iso-8859-1
# ๐ bestie option alert: when replying, first try the same charset the original poster used!
set reply-in-same-charset
# when replying, don't mash From: and To: together. Old From: becomes new To:, and old To: gets merged into Cc: ๐ซถ
set recipients-in-cc
# when sending, wait for the Mail-Transfer-Agent to finish. Only then can you catch errors through its exit status
# (the built-in SMTP one counts too)! ๐
set sendwait
# only built-in MIME types, no mime.types(5) files, thank you ๐
set mimetypes-load-control
# default workspace, relative to $HOME ๐
set folder=mail
# a leading + usually means "inside folder" โจ
# record is where copies of sent messages get saved ๐
set MBOX=+mbox.mbox DEAD=+dead.txt \
record=+sent.mbox record-files record-resent
# make file mymbox and file myrec land in the right spots ๐ฆ
shortcut mymbox %:+mbox.mbox myrec +sent.mbox
# technically required, especially for S/MIME ๐
set from='Your Name <address@exam.ple>'
# hostname and/or smtp-hostname might be needed if the mta SERVER and the domain of from don't match.
# The urlencode command can encode USER and PASS ๐
set mta=(smtps?|submissions?)://[USER[:PASS]@]SERVER[:PORT] \
smtp-auth=login/plain... \
smtp-use-starttls
# never refuse to start into interactive mode, plus a whole bunch of goodies ๐ธ
set emptystart \
colour-pager crt= \
followup-to followup-to-honour=ask-yes fullnames \
history-file=+.s-nailhist history-size=-1 history-gabby \
mime-counter-evidence=0b1111 \
prompt='?\$?!\$!/\$^ERRNAME[\$account#\$mailbox-display]? ' \
reply-to-honour=ask-yes \
umask=
# only keep these header fields when typing messages ๐
headerpick type retain from_ date from to cc subject \
message-id mail-followup-to reply-to
# ...when forwarding messages ๐
headerpick forward retain subject date from to cc
# ...when saving messages, etc. (commented out for now) ๐
#headerpick save ignore ^Original-.*$ ^X-.*$
# some mailing lists ๐ฌ
mlist '@xyz-editor\.xyz$' '@xyzf\.xyz$'
mlsubscribe '^xfans@xfans\.xyz$'
# handle a few file extensions (to store MBOX databases) ๐ง
filetype bz2 'bzip2 -dc' 'bzip2 -zc' \
gz 'gzip -dc' 'gzip -c' xz 'xz -dc' 'xz -zc' \
zst 'zstd -dc' 'zstd -19 -zc' \
zst.pgp 'gpg -d | zstd -dc' 'zstd -19 -zc | gpg -e'
# a real-life example from a very huge free mail provider ๐
# instead of stuffing content straight into account, we define a macro, so we can switch
# "accounts" from within on-compose-splice, for example!
define XooglX {
set folder=~/spool/XooglX inbox=+syste.mbox sent=+sent
set from='Your Name <address@examp.ple>'
set pop3-no-apop-pop.gmXil.com
shortcut pop %:pop3s://pop.gmXil.com
shortcut imap %:imaps://imap.gmXil.com
# or, entirely IMAP based setup ๐
#set folder=imaps://imap.gmail.com record="+[Gmail]/Sent Mail" \
# imap-cache=~/spool/cache
set mta=smtp://USER:PASS@smtp.gmXil.com smtp-use-starttls
# alternatively:
set mta=smtps://USER:PASS@smtp.gmail.com:465
}
account XooglX {
\call XooglX
}
# this one's chunky ๐: it won't let you send if the domain name doesn't match on the SMTP protocol level.
# That would bite if the value of from doesn't match, e.g. people with a sXXXXeforge project who want to talk
# to the mailing list under their project account (in from), but still send through their normal mail provider
define XandeX {
set folder=~/spool/XandeX inbox=+syste.mbox sent=+sent
set from='Your Name <address@exam.ple>'
shortcut pop %:pop3s://pop.yaXXex.com
shortcut imap %:imaps://imap.yaXXex.com
set mta=smtps://USER:PASS@smtp.yaXXex.com:465 \
hostname=yaXXex.com smtp-hostname=
}
account XandeX {
\call Xandex
}
# make some new commands, like `ls /tmp' ๐ฆ
commandalias lls '!ls ${LS_COLOUR_FLAG} -aFlrS'
commandalias llS '!ls ${LS_COLOUR_FLAG} -aFlS'
set pipe-message/external-body='?* echo $MAILX_EXTERNAL_BODY_URL'
# no direct gpg(1) support yet, bummer ๐ญ but simple --clearsign'd message parts can be handled like this:
define V {
localopts yes
wysh set pipe-text/plain=$'?*#++=?\
< "${MAILX_FILENAME_TEMPORARY}" awk \
-v TMPFILE="${MAILX_FILENAME_TEMPORARY}" \'\
BEGIN{done=0}\
/^-----BEGIN PGP SIGNED MESSAGE-----/,/^$/ {\
if(done++ != 0)\
next;\
print "--- GPG --verify ---";\
system("gpg --verify " TMPFILE " 2>&1");\
print "--- GPG --verify ---";\
print "";\
next;\
}\
/^-----BEGIN PGP SIGNATURE-----/,\
/^-----END PGP SIGNATURE-----/{\
next;\
}\
{print}\
\''
print
}
commandalias V '\'call V
Okay, password safety time ๐ when you store passwords in ~/.mailrc, you should set proper permissions on that file with $ chmod 0600 ~/.mailrc. If the optional netrc-lookup feature is available, you can stash your credentials in the central ~/.netrc file instead. Here's a different version of the example account that sets up SMTP and POP3:
define XandeX {
set folder=~/spool/XandeX inbox=+syste.mbox sent=+sent
set from='Your Name <address@exam.ple>'
set netrc-lookup
# load an encrypted ~/.netrc by uncommenting the next line
#set netrc-pipe='gpg -qd ~/.netrc.pgp'
set mta=smtps://smtp.yXXXXx.ru:465 \
smtp-hostname= hostname=yXXXXx.com
set pop3-keepalive=240 pop3-no-apop-pop.yXXXXx.ru
commandalias xp fi pop3s://pop.yXXXXx.ru
}
account XandeX {
\call XandeX
}
And, in the ~/.netrc file:
machine *.yXXXXx.ru login USER password PASS
This configuration should now work just fine โจ
$ echo text | s-nail -dvv -AXandeX -s Subject user@exam.ple
S/MIME step by step ๐
The first thing you need for โSigned and encrypted messages with S/MIMEโ (an optional feature) is a personal certificate and a private key. The certificate holds public info, like your name and email address(es), plus the public key that others can use to encrypt messages for you (the owner of the private key), and to verify messages signed with that certificate's private key. The certificate gets included in every signed message, but the private key must be kept secret ๐คซ. It's what decrypts messages that were encrypted with the public key, and what signs your messages.
For personal use, it's recommended to get an S/MIME certificate from one of the major CAs on the Internet. Lots of CAs offer these for free ๐. What's usually offered is a combined certificate and private key in PKCS#12 format, which S-nail does not accept directly. To convert it to PEM format, you can use the following shell commands. Please read on for how to actually use these PEM files:
$ openssl pkcs12 -in cert.p12 -out certpem.pem -clcerts -nodes
$ # Alternatively
$ openssl pkcs12 -in cert.p12 -out cert.pem -clcerts -nokeys
$ openssl pkcs12 -in cert.p12 -out key.pem -nocerts -nodes
There's also https://www.CAcert.org, which issues client and server certificates to members of their community for free ๐. Their root certificate (https://www.cacert.org/certs/root.crt) is often not in the default set of trusted CA root certificates, though. That means you have to download it separately, and it needs to be part of the S/MIME certificate validation chain, either by including it in smime-ca-dir or as a vivid member of the smime-ca-file. But let's take a step-by-step tour of how to set up S/MIME with a CAcert.org certificate despite this situation!
First of all, you'll need to become a member of the CAcert.org community, which is as simple as registering yourself via the web interface ๐ซถ. Once you're in, create and verify all the email addresses you want to be able to create signed and encrypted messages for or with, using the corresponding entries of the web interface. Now you're ready to create S/MIME certificates! So let's create a new โclient certificateโ. Make sure to include all the email addresses that should be covered by the certificate in the following web form, and also use your name as the โcommon nameโ.
Create a private key and a certificate request on your local computer. (Please check the manual pages of the commands used for the in-depth scoop on what the arguments etc. do ๐):
$ openssl req -nodes -newkey rsa:4096 -keyout key.pem -out creq.pem
Afterwards, copy and paste the content of โcreq.pemโ into the certificate-request (CSR) field of the web form on the CAcert.org website. You may need to unfold some โadvanced optionsโ to see that text field ๐. This last step makes sure your private key (which never left your box) and the certificate belong together, through the public key that finds its way into the certificate via the certificate request. You're now ready to create your CAcert certified certificate. Download it and store it, or copy and paste it, as โpub.crtโ ๐ .
Yay ๐ฅณ To use your new S/MIME setup, you now need to create a combined private key and public key (certificate) file:
$ cat key.pem pub.crt > ME@HERE.com.paired
This is the file S-nail will work with. If you created your private key with a passphrase, S-nail will ask you for it whenever a message is signed or decrypted. The exception is when this has been automated, as described in โSigned and encrypted messages with S/MIMEโ. Set the following variables to henceforth use S/MIME. (Setting smime-ca-file is only of interest for verification ๐ฆ):
? set smime-ca-file=ALL-TRUSTED-ROOT-CERTS-HERE \
smime-sign-cert=ME@HERE.com.paired \
smime-sign-digest=SHA512 \
smime-sign from=myname@my.host
๐ Using CRLs with S/MIME or TLS ๐
[Option] Okay so, certification authorities (CAs) issue certificate revocation lists (CRLs) on a regular basis ๐. These lists are basically the serial numbers of certificates that got declared invalid after they were already issued ๐ โโ๏ธ. This usually happens because the private key for the certificate got compromised, or because the owner left the organization named in the certificate, etc. ๐
If you're seriously using S/MIME or TLS verification, you NEED an up-to-date CRL for each trusted CA, no cap ๐ฅ. Otherwise there's zero way to tell valid certs from invalidated ones ๐ญ. S-nail currently offers no mechanism to fetch CRLs, nor to access them on the Internet, so you have to grab them with some external mechanism ๐ซถ.
S-nail only accepts CRLs in PEM format ๐. CRLs in DER format have to be converted first, like, e.g.:
$ openssl crl -inform DER -in crl.der -out crl.pem
To tell S-nail about your CRLs, make a directory that contains all CRL files (and no other files) ๐๏ธ. Then set the smime-crl-dir variable for S/MIME or the tls-crl-dir variable for TLS so they point to that directory ๐ง. After that, S-nail requires a CRL to be present for each CA that is used to verify a certificate.
โจ FAQ โจ
In general it's a good idea to turn on debug (-d) and / or verbose (-v, twice) if something isn't working right ๐. Super often a diagnostic message shows up that leads you straight to the solution ๐
.
๐ธ S-nail shortly hangs on startup ๐ธ
This can have two reasons ๐ฅบ. One is that it has to wait for a file lock, and that can't be helped. The other is that S-nail calls the function uname(2) to query the nodename of the box, because sometimes the real one is needed instead of the one represented by the internal variable hostname.
You may have varying success by making sure the real hostname and โlocalhostโ have entries in /etc/hosts. Or, more generally, make sure the name service is properly set up ๐ ๏ธ. Does hostname(1) return the expected value? Does this local hostname have a domain suffix? RFC 6762 standardized the link-local top-level domain โ.localโ, so try again after adding an (additional) entry with this extension ๐.
๐ฆ I cannot login to Google mail (via OAuth) ๐ฆ
Since 2014, some free service providers classify programs as โless secureโ unless they use a special authentication method (OAuth 2.0). That method wasn't standardized for non-HTTP protocol authentication token query until August 2015 (RFC 7628) ๐ค.
It's different from Kerberos / GSSAPI, which has been in development since the mid of the 1980s. With Kerberos you can easily create a local authentication ticket for yourself with the locally installed kinit(1) program. OAuth has no such local part. Instead it needs a world-wide-web query to create or fetch a token, and since there's no local cache, that query would have to happen every time S-nail is invoked (in interactive sessions it may be a bit different).
S-nail does not directly support OAuth. It does, however, support XOAUTH2 / OAUTHBEARER, see โBut, how about XOAUTH2 / OAUTHBEARER?โ If you don't use that, you have to declare S-nail a โless secure appโ (on the provider's account web page) in order to read and send mail ๐. It also seems possible to take the following steps instead:
- hand the provider a mobile phone number ๐ฑ,
- enable โ2-Step Verificationโ,
- create an application specific password (16 characters), and
- use that special password instead of your real Google account password in S-nail (for more on that, see the section โOn URL syntax and credential lookupโ).
๐ But, how about XOAUTH2 / OAUTHBEARER? ๐
Following up on โI cannot login to Google mail (via OAuth)โ, there's one OAuth-based method available ๐: the OAuth 2.0 bearer token usage standardized in RFC 6750 (with the SASL mechanism from RFC 7628), also known as XOAUTH2 and OAUTHBEARER. It lets you fetch a temporary access token via the web, which you can then use locally as a password. The protocol is simple and extendable. In theory, token updates or even password changes could happen via a simple TLS secured server login ๐ค, but today you need a web browser and an external support tool to use this method. The token times out and has to be refreshed periodically via the web โฐ.
Some hurdles must be taken before you can use this method ๐งโโ๏ธ. Using GMail as an example, you have to register an application (that is, a name). For it you need credentials, a โclient IDโ and a โclient secretโ, which need to be created and saved locally (in a secure way). These initial configuration steps can be performed at https://console.developers.google.com/apis/credentials. After that, a refresh token can be requested. A python program to do this for GMail accounts is https://github.com/google/gmail-oauth2-tools/raw/master/python/oauth2.py:
$ python oauth2.py --user=EMAIL \
--client-id=THE-ID --client-secret=THE-SECRET \
--generate_oauth2_token
To authorize token, visit this url and follow the directions:
https://accounts.google.com/o/oauth2/auth?client_id=...
Enter verification code: ...
Refresh Token: ...
Access Token: ...
Access Token Expiration Seconds: 3600
$ # Of which the last three are actual token responses.
$ # Thereafter access tokens can regularly be refreshed
$ # via the created refresh token (read on)
Heads up, bestie: the refresh token must be saved locally too, and it needs to be stored securely ๐.
The whole procedure can be read at https://github.com/google/gmail-oauth2-tools/wiki/OAuth2DotPyRunThrough. Since periodic timers aren't supported yet, keeping an access token up-to-date from within S-nail can only be done via the hook on-main-loop-tick, or (for sending only) on-compose-enter โจ. For more on authentication, please see the section โOn URL syntax and credential lookupโ:
set on-main-loop-tick=o-m-l-t on-compose-enter=o-c-e
define o-m-l-t {
xcall update_access_token
}
define o-c-e {
xcall update_access_token
}
set access_token_=0
define update_access_token {
local set i epoch_sec epoch_nsec
vput vexpr i epoch
eval set $i # set epoch_sec/_nsec of vexpr epoch
vput vexpr i + $access_token_ 2100
if $epoch_sec -ge $i
vput ! password python oauth2.py --user=EMAIL \
--client-id=THE-ID --client-secret=THE-SECRET \
--refresh-token=THE-REFRESH-TOKEN |\
sed '1b PASS;d; :PASS s/^.\{1,\}:\(.\{1,\}\)$/\1/'
vput csop password trim "$password"
if -n "$verbose"
echo password is <$password>
endif
set access_token_=$epoch_sec
endif
}
๐ Not "defunctional", but the editor key does not work ๐
Two possibilities, bestie ๐ค. The first is a shadowed sequence. Setting debug, or the most verbose mode possible, makes S-nail print out the bind tree after it's built. Since that's a cache, this only happens on startup or after you modify bindings.
Or second ๐ : terminal libraries (see โOn terminal control and line editorโ, bind, termcap) may report different codes than the terminal actually sends. That makes bindings dysfunctional, because expected and received data don't match. The verbose listing of bindings will show the byte sequences that are expected. (One super common culprit is that the keypad, which may not even exist, isn't turned on. Then the resulting layout reports the keypad control codes for the normal keyboard keys ๐ต.)
To get around it, use for example the program cat(1) with its option -v, if available, to see the byte sequences that are actually produced by keypresses. Then use the variable termcap to make S-nail aware of them ๐ง . The terminal I'm typing this on is a little chaotic ๐ and produces some unexpected sequences. For example, here's the shifted home key:
? set verbose
? bind*
# 1B 5B=[ 31=1 3B=; 32=2 48=H
bind base :kHOM z0
? x
$ cat -v
^[[H
$ s-nail -v -Stermcap='kHOM=\E[H'
? bind*
# 1B 5B=[ 48=H
bind base :kHOM z0
๐ง Can S-nail git-send-email? ๐ง
Yes! ๐ Put (at least parts of) the following in your ~/.gitconfig:
[sendemail]
smtpserver = /usr/bin/s-nail
smtpserveroption = -t
#smtpserveroption = -Sexpandaddr
smtpserveroption = -Athe-account-you-need
##
suppresscc = all
suppressfrom = false
assume8bitEncoding = UTF-8
#to = /tmp/OUT
confirm = always
chainreplyto = true
multiedit = false
thread = true
quiet = true
annotate = true
Newer git(1) versions (v2.33.0) added the option sendmailCmd. Patches can also be sent directly ๐, for example:
$ git format-patch -M --stdout HEAD^ |
s-nail -A the-account-you-need -t RECEIVER
๐ Howto handle stale dotlock files ๐
folder sometimes fails to open MBOX mail databases, because creating โdotlock filesโ is impossible due to existing but unowned lock files ๐. S-nail doesn't offer an option to deal with those files, because it's considered a site policy what counts as unowned and what doesn't. The site policy is usually defined by administrator(s) and expressed in the configuration of a locally installed MTA (for example Postfix โstale_lock_time=500sโ). Therefore, the suggestion:
$ </dev/null s-nail -s 'MTA: be no frog, handle lock' $LOGNAME
By sending a mail to yourself, the local MTA can use its normal queue mechanism to try the delivery multiple times, finally decide a lock file has become stale, and remove it ๐.
โจ IMAP CLIENT โจ
okay bestie, so [Option]ally there is IMAP client support available ๐ But this part is obsolete and is set to vanish in v15 along with the big MIME and I/O layer rewrite ๐ญ It uses old-style blocking I/O and leans way too hard on signal based long code jumps. Support can hopefully be readded later on new-style I/O, with SysV signal handling ๐ธ Fun fact: the IMAP support was already yeeted from the codebase, then brought back because users asked for it ๐ซถ So the IMAP code is basically at the level of S-nail v14.8.16 (with imapcodec being the sole exception), and you should treat it with some care ๐
IMAP uses the imap:// and imaps:// protocol prefixes, and an IMAP-based folder may be used โจ IMAP URLs (paths) get inspected and possibly transformed before use, and the command imapcodec can manually apply those transformations to any argument you give it ๐ง Hierarchy delimiters get normalized. That step is configurable through the imap-delim variable chain, but it defaults to the first delimiter it sees ๐ฆ S-nail handles internationalised IMAP names too, en- and decoding them to and from the ttycharset as necessary and possible ๐ If a mailbox name gets expanded (see โFilename transformationsโ) to an IMAP mailbox, names beginning with + refer to IMAP mailboxes below the folder target box, while folder names prefixed by @ refer to folders below the hierarchy base. So when you're in an IMAP mailbox, folders @ lists all folders below the current one ๐
Note, bestie: some IMAP servers won't let you create mailboxes in the hierarchy base and insist they be made as subfolders of INBOX. With those servers, a folder name of this form should be used (the last character is the server's hierarchy delimiter) ๐
imaps://mylogin@imap.myisp.example/INBOX.
The following IMAP-specific commands exist ๐
IMAP-specific commands
cacheโ only works on cached IMAP mailboxes ๐ Takes a message list and reads those messages into the IMAP cache.connectโ if you're in disconnected mode on an IMAP mailbox, this switches you back to online mode and connects to the mail server while keeping the mailbox status โจ Check thedisconnectedvariable for more info.disconnectโ if you're in online mode on an IMAP mailbox, this switches you to disconnected mode and keeps the mailbox status ๐ฆ See thedisconnectedvariable for more. You can optionally give it a list of messages, and those get read into the cache before the connection closes. Sodisco *makes the entire mailbox available for disconnected use ๐imapโ sends command strings directly to the current IMAP server ๐ S-nail always operates in IMAP selected state on the current mailbox, so commands that change that will produce undesirable results and should be avoided ๐ญ Useful IMAP commands are:createโ takes the name of an IMAP mailbox as an argument and creates it ๐getquotaroot(RFC 2087) โ takes the name of an IMAP mailbox as an argument and prints the quotas that apply to it ๐ธ Not every IMAP server supports this one.namespace(RFC 2342) โ takes no arguments and prints the Personal Namespaces, the Other User's Namespaces and the Shared Namespaces ๐ง Each namespace type shows up in parentheses. If there are multiple namespaces of the same type, inner parentheses separate them. For each namespace you get a prefix and a hierarchy separator. Not every IMAP server supports this one either.
imapcodecโ performs IMAP path transformations ๐ฅ It supportsvput(see โCommand modifiersโ) and manages the error number!. The first argument picks the operation:e[ncode]normalizes hierarchy delimiters (seeimap-delim) and converts strings from the localettycharsetinto the internationalized variant IMAP uses, whiled[ecode]does the reverse. Encoding will honour the (global) value ofimap-delimโจ
IMAP-specific internal variables
disconnected(Boolean) โ when an IMAP mailbox is selected and this is set, no connection to the server gets initiated ๐ซถ Instead, data comes from the local cache (seeimap-cache). Mailboxes that aren't in the cache, and messages that haven't been fully fetched from the server yet, are not available. To fetch every message in a mailbox at once, you can usecopy * /dev/nullwhile still in connected mode ๐ฆ Changes you make to IMAP mailboxes in disconnected mode get queued and committed later, once a connection to that server is made.
This procedure is not completely reliable, bestie ๐ญ It can't be guaranteed that the IMAP unique identifiers (UIDs) on the server still match the ones in the cache by then. When that problem happens, the data is saved to DEAD.
disconnected-USER@HOSTโ the specified account gets handled as described for thedisconnectedvariable above, but other accounts are not affected ๐imap-auth-USER@HOST,imap-authโ sets the IMAP authentication method ๐ The default islogin(some servers call itplain). The other options are: [v15-compat]oauthbearer(see the โFAQโ entry โBut, how about XOAUTH2 / OAUTHBEARER?โ), [v15-compat]externalandexternanon(for TLS secured connections that pass a client certificate viatls-config-pairs), plus the [Option]alcram-md5andgssapiโจ All methods need a user and a password, exceptgssapiandexternal, which only need the user ๐ฅexternanonbuilds solely on the credentials passed via a client certificate. It's usually the way to go, since tested servers don't actually follow RFC 4422 and will fail if extra credentials get passed ๐imap-cacheโ enables caching of IMAP mailboxes ๐ The value must point to a directory that either already exists or can be created by S-nail. S-nail can delete all the contents of the cache at any time, so it is not safe to make assumptions about them ๐imap-delim-USER@HOST,imap-delim-HOST,imap-delimโ the hierarchy separator the IMAP server uses ๐ง Whenever an IMAP path is specified, it goes through normalization, and one of those steps is squeezing and adjusting hierarchy separators. If this variable is set, any character from its value that shows up in the path gets replaced by the first member of the value. An empty value makes the default kick in, which is/.๐ If it isn't set, S-nail reuses the first hierarchy separator character it finds in a user-given mailbox name.imap-keepalive-USER@HOST,imap-keepalive-HOST,imap-keepaliveโ IMAP servers may close the connection after a stretch of inactivity ๐ธ The standard says this has to be at least 30 minutes, but practical experience may vary. Set this to a numeric value greater than0and aNOOPcommand gets sent every value seconds when no other operation is happening ๐imap-list-depthโ when retrieving the list of folders on an IMAP server, thefolderscommand stops once it reaches a certain depth to avoid possible infinite loops ๐ฆ This variable sets the maximum depth allowed. The default is2. If the folder separator on the current IMAP server is a slash/, this variable has no effect and thefolderscommand won't descend into subfolders at all ๐imap-use-starttls-USER@HOST,imap-use-starttls-HOST,imap-use-starttlsโ makes S-nail issue aSTARTTLScommand to turn an unencrypted IMAP session into a TLS encrypted one ๐ฅ Not every server supports this, and it isn't used if the session is already encrypted by the IMAPS method ๐ซถ
โจ SEE ALSO โจ
iykyk, these are the vibes you'll want to check out next ๐ bogofilter(1), gpg(1), more(1), newaliases(1), openssl(1), sendmail(1), sh(1), spamassassin(1), iconv(3), setlocale(3), aliases(5), termcap(5), terminfo(5), locale(7), mailaddr(7), re_format(7) (or regex(7)), mailwrapper(8), sendmail(8) ๐
โจ HISTORY โจ
okay so this is the lore section and it's giving legacy ๐ง M. Douglas McIlroy wrote in his article โA Research UNIX Reader: Annotated Excerpts from the Programmer's Manual, 1971-1986โ that a mail(1) command already showed up in First Edition Unix in 1971. Electronic mail was there from day one ๐ and everybody kept poking at it for safe simultaneous access, better privacy, surviving crashes, using uucp, screening out foreign freeloaders, or whatever. The interface didn't change until v7 (Thompson). Later, once mail went global, Dave Presotto took charge and got communications organized with a grab-bag of external networks (v8).
BSD Mail, which was largely compatible with Unix mail, was written in 1978 by Kurt Shoens and developed as part of the BSD Unix distribution until 1995. This manual page is derived from โThe Mail Reference Manualโ that Kurt Shoens wrote for Mail 1.3, included in 3BSD in 1980. The common Unix and BSD denominator got standardized as mailx(1) in the X/Open Portability Guide Issue 2 (January 1987). After the rise of Open Source BSD variants, Mail saw continuous development in the individual code forks, noticeably by Christos Zoulas in NetBSD. Based upon this Nail, later Heirloom Mailx, was developed by Gunnar Ritter in the years 2000 until 2008. Since 2012 S-nail is maintained by Steffen Nurpmeso. ๐
Electronic mail exchange in general is an even older concept ๐. The earliest well documented electronic mail system was part of the Compatible Time Sharing System (CTSS) at MIT. Its MAIL command had been proposed in a staff planning memo at the end of 1964 and was implemented in mid-1965 when Tom Van Vleck and Noel Morris wrote the necessary code. Similar communication programs were built for other timesharing systems. One of the most ambitious and influential was Murray Turoff's EMISARI. Created in 1971 for the United States Office of Emergency Preparedness, EMISARI combined private electronic messages with a chat system, public postings, voting, and a user directory.
During the 1960s it was common to connect a large number of terminals to a single, central computer. Connecting two computers together was relatively unusual. This began to change with the development of the ARPANET, the ancestor of today's Internet. In 1971 Ray Tomlinson adapted the SNDMSG program, originally developed for the University of California at Berkeley timesharing system, so it could transmit a message across the network into the mailbox of a user on a different computer. For the first time you had to specify the recipient's computer as well as an account name. Tomlinson decided that the underused commercial at โ@โ would work to separate the two. genius, honestly ๐ฆ
Sending a message across the network was originally treated as a special instance of transmitting a file, so a MAIL command was included in RFC 385 on file transfer in 1972. Because it was not always clear when or where a message had come from, RFC 561 in 1973 aimed to formalize electronic mail headers, including โfromโ, โdateโ, and โsubjectโ. In 1975 RFC 680 described fields to help with sending messages to multiple users, including โtoโ, โccโ, and โbccโ. In 1977 these features and others went from best practices to a binding standard in RFC 733. And the moment that really sealed it: Queen Elizabeth II of England became the first head of state to send electronic mail on March 26 1976 while ceremonially opening a building in the British Royal Signals and Radar Establishment (RSRE) in Malvern. royalty really said "send" ๐
โจ AUTHORS โจ
The main cast ๐ Kurt Shoens, Edward Wang, Keith Bostic, Christos Zoulas, Gunnar Ritter.
S-nail is developed by Steffen Nurpmeso, reachable at s-mailx@lists.sdaoden.eu. ๐ซถ
โจ CAVEATS โจ
โ ๏ธ [v15 behaviour may differ] Heads up, bestie: interrupting an operation viaSIGINTaka โcontrol-Cโ from anywhere other than a command prompt is very problematic and can leave the program in an undefined state ๐. Many library functions cannot deal with thesiglongjmp(3)that this software (still) performs. Efforts have been made to address this, but no sooner than v15 will it have been worked out. Interruptions have not been disabled, so you can still forcefully break hanging network connections, for example. All of this is unrelated to ignore.
The SMTP and POP3 protocol support of S-nail is very basic ๐ธ. Also, if it fails to contact its upstream SMTP server, it will not make further attempts to transfer the message at a later time (setting save and sendwait may be useful). If this is a concern, it might be better to set up a local SMTP server that is capable of message queuing.
โจ BUGS โจ
Okay, the messy stuff ๐
When a network-based mailbox is open, directly changing to another network-based mailbox of a different protocol (i.e., from POP3 to IMAP or vice versa) will cause a โdeadlockโ. ๐
After deleting some message of a POP3 mailbox, the header summary falsely claims that there are no messages to display. You need to perform a scroll or dot movement to restore proper state.
In โthreadโed sort mode a power user may encounter crashes very occasionally (this is may and very). ๐ญ
Please report bugs to the contact-mail address, for example from within s-nail: ? eval mail $contact-mail. Including the verbose output of the command version may be helpful:
? wysh set escape=! verbose; vput version xy; unset verbose;\
eval mail $contact-mail
Bug subject
!I xy
!.
Information on the web is available by running $ s-nail -X 'echo $contact-web; x'. ๐
โจ That's the whole page, bestie! ๐ซถ This copy is from the Debian build of S-NAIL(1), dated June 27, 2024. Stay iconic and read the manual, no cap ๐ ๐
๐ฆ where this page comes from
- fedora 43: s-nail 14.9.25-3.fc43 ยท ISC AND BSD-4-Clause-UC AND BSD-3-Clause AND HPND-sell-variant
- fedora 44: s-nail 14.9.25-4.fc44 ยท ISC AND BSD-4-Clause-UC AND BSD-3-Clause AND HPND-sell-variant
- ๐ the page's license: ISC
- Copyright (c) 2012 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms