filtermail(1)
π NAME π
filtermail β filter incoming e-mail into accepted, spam or ignored π
and yeah bestie this is the filtermail(1) manual page, so buckle up π
β¨ SYNOPSIS β¨
filtermail [OPTIONS] base
[OPTIONS] - cf. section OPTIONS
base: the absolute path to the userβs home directory
so base is just the absolute path to the userβs home directory, periodt. and whenever paths show up in options, or in the configuration file, they're allowed to start with ~/ β and if they do, that little ~ gets replaced by base. slay β¨
π¦ DESCRIPTION π¦
okay so here's the whole vibe: filtermail filters incoming e-mail as either accepted, spam, or ignored. it does this with rule files, which get inspected one after another until the incoming e-mail matches a rule. the second that happens, the rule's associated action (accept, spam, or ignore) is executed. and if no rule matches the e-mail at all? then the e-mail is simply accepted. no drama π
alternatively β and this is such a main character moment β when you use the option --inspect, filtermail can be used to find the domain name of the sender, its IP address, its country of origin and the cidr-range containing the received IP address (see sections OPTIONS and FILTERMAIL INSPECT below) π
Accepted e-mail normally gets appended to the mail file which the incoming mail server uses when receiving mail for the current user. like, if the user's username is frank, then incoming mail is appended to the file /var/mail/frank. users may also define directories to contain saved e-mails (e.g., ~/Mail), and filtermail can be configured to append e-mail considered as spam to, e.g., ~/Mail/spam. same energy for e-mail matching the 'ignore' criteria β that could be appended to ~/Mail/ignore. bestie, it gets better: instead of appending the complete e-mail to its destination file, the received e-mail's From: and Subject: headers can be appended to its destination file instead. how? by prefixing :HDRS: to the name of the destination file π
alternatively, such e-mail can also be ignored β losing it completely β by just not specifying a destination file. and the option to merely log the received e-mail's From: and Subject: headers may come in handy if the received e-mail is also kept elsewhere (e.g., in another account, which forwards the received e-mail to the computer running filtermail) and the ignoring rules might result in occasional false positive decisions. (see also section OPTIONS below).
Filtermail uses three types of files:
- The configuration file contains values of options which are generally used (see sections CONFIGURATION and OPTIONS);
- Mail filtering rules are hierarchically ordered in the rules file: incoming mail is sequentially matched against the patterns defined in files specified in the rules file until a match is found. once a match has been found, the rule's action (
accept,ignoreorspam) is executed, ending the filtering process (see section RULES); - Each file specified in the rules file defines matching patterns, which are tested sequentially. testing those patterns ends once the incoming mail matches a pattern. the result of the matching is forwarded to the rules file, which may result in executing the rule's action (see section PATTERNS).
β οΈ okay important one: if filtermail detects a syntax error in the rules file or in a rule specification file, the incoming mail is accepted. to avoid this situation, the --syntax option (see section OPTIONS) should be used when modifying, adding or removing rule files, so you can verify that the specified rules were correctly formulated. do not skip this, bestie π
and to actually use filtermail, the incoming mail server must recognize it as a valid mail handling program (see section EXAMPLES). that's the vibe check right there π
π CONFIGURATION π
Options (see section OPTIONS) that are not flagged with NO_CONFIG can also be specified in a configuration file. by default, the configuration file ~/.filtermail/config is used β¨
Command line options always take precedence over specifications in the configuration file. always. no negotiating π
The configuration file must exist, but it may be empty. it must exist because its directory defines the directory where the files defining the filtering rules are located. empty lines, and the content of lines starting at the #-character, are ignored.
Option --expire is used to remove patterns whose date stamps (cf. section PATTERNS) indicate dates before the date specified by --expire. the configuration file may contain dont-expire: lines. each dont-expire: line specifies the name of a pattern file whose entries don't expire. files specified in dont-expire: lines may not exist, but to avoid inspecting pattern files for expired dates, the name(s) of those files must be identical to the names of the files used in the rules file (cf. section RULES below) π
Other files in the configuration file may use the [~]/path format, or must be plain filenames (i.e., not starting with /-characters). plain files are relative to the directory containing the configuration file (cf. section EXAMPLES).
π RULES π
All mail filtering rules are defined in the rules file. mail filtering starts at the first rule, and keeps going until either the incoming e-mail matches a rule, or until all rules have been processed and the e-mail does not match any rule. in that latter case, the e-mail is considered accepted. lucky them π
Empty lines, and lines whose first non-blank character is a #-character, are ignored. the rules themselves cannot contain #-characters.
Rules are written according to the following syntax (elements between square brackets are optional, the content of bracketed sections followed by a * character may be repeated (not using the square brackets). lowercase words are keywords and cannot be used otherwise. capitalized words are described below. each rule is specified on its own line. line continuation (using, e.g., \ at the end of a line) is not supported). here's the rule's syntax:
if Header File [and Header File]* Action
Header is the name of a mail header (e.g., From:, Received:). header specifications must be identical to the first words of header lines of the received e-mail. so to match the From header in the e-mail's first line, specify From (i.e., no colon). some headers have variants. e.g., Received: and Received-SPF:. to select all headers sharing their initial characters, append a +-character to the initial part (e.g., to select all headers starting with Received use Received+, and use From+ to select all From: headers including the e-mail's first line) β¨
File is the name of the file containing patterns to inspect. filenames must start with ./ and define the locations of files below the configuration file's directory. e.g., ./spam/subject specifies the file subject in a subdirectory spam containing patterns considered by the rule π¦
Action specifies the action to execute when e-mail matches a rule. Action can be:
accept: the e-mail is accepted (i.e., appended to the accept location specified in the configuration file) πignore: the e-mail is ignored (i.e., appended to the ignore location specified in the configuration file) πspam: the e-mail is considered spam (i.e., appended to the spam location specified in the configuration file) π₯
πΈ PATTERNS πΈ
okay bestie, THIS is where it gets fun. so β the files you name in your rules files define patterns, and those patterns are the things that might be hanging out in the headers your rules point at. here's the workflow:
the header lines get selected by the Header specs in the rules file, and then they get matched against those patterns after the header labels get yanked off. so a line like Subject: hello world gets handed to the patterns as the (trimmed) line hello world. cute, right? π
and the second a header's content matches a pattern, inspection ends with a successful match β boom, done, periodt. (unless the rule itself specifies not, in which case a successful match means the rule gets a failing match. oop π)
π comments & the lil' shuffle π
pattern files can start with a file-specific comment, meaning: empty lines, plus any line whose first non-blank character is #, all the way up to a comment line that is literally equal to #=. and the patterns themselves can ALSO be preceded by comment lines. no rules against it, we're chill π
now here's the iconic bit: once a pattern is matched, it moves one position upward in the pattern file β and it drags its associated comment along with it. main character behavior, honestly π
β¨ how you write a pattern β¨
pattern specs use this syntax. quick notes: elements between square brackets are optional (and when you use them, you do NOT type the square brackets), capitalized words get explained below, every pattern lives on a single line, and line continuation is not supported, sorry:
Nr Date Expression [and Expression]*
this pattern indicates a match when all Expressions match. all of them. no skips.
this syntax uses these elements:
Nrβ the number of times email was successfully matched by the pattern. every successful match bumps the count (capping out at a maximum of999), and the pattern rises one position in the pattern file. the field width of the number field must be at least 3 character positions wide. when you're adding a pattern, use1.Dateβ the date of the most recent match, formattedyy-mm-dd, like23-03-31. adding a new pattern? the current date could be used.Expressionβ defines at least one matching mode and the associated expression. yeah we're going one level deeper, hold on π¦
π«Ά the Expressions themselves π«Ά
they use this syntax:
MatchMode [not] Spec
the selected headers get matched against Spec using the MatchMode you chose. not is optional β when you specify it (just omit the square brackets), the result of the match gets negated. when multiple Expression specs are joined by and keywords, the final pattern results in a match if all the Expression specs indicate a match. and the moment one Expression does not indicate a match, subsequent Expressions are not evaluated and the headers do not match the pattern. early exit, queen behavior β¨
when you use not, the email may not match the Spec specification. e.g., when email should contain a To: header or a Cc: header, this rule works (cf. section EXAMPLES:):
if To: ./match/noto and Cc: ./match/noto spam
with match/noto being:
1 23-05-10 p not β.β
watch out bestie βSpecmust be surrounded by single quotes. need a single quote inside a Spec? escape it as\'. and in general: the character following a backslash is used as-is, with the backslash removed from the Spec β e.g., to construct a Spec containing'\n, specify\'\\n.
π the MatchModes π
there are five types of MatchModes (the page says five and we report the news, don't @ me πΈ). MatchModes that use regular expressions use extended regular expression patterns: prefix multipliers and bounding-characters get backslashes when they should be interpreted as ordinary characters β i.e., *, +, ?, ^, $, |, (, ), [, ], {, } should be escaped when used as literal characters.
cβ (cidr) matches[d{1.3}.d{1.3}.d{1.3}.d{1.3}]IP address patterns found in selected headers against theSpecpattern, e.g.'1.22.333.0/24'.iβ (ignore case) matches the selected headers case-insensitively against theSpecpattern, e.g.'yes'.nβ (no-case pattern) matches the selected headers case-insensitively against theSpecregular expression pattern, e.g.'news.*\.*@reply'.pβ (pattern) matches the selected headers (case-sensitively) against theSpecregular expression pattern.sβ (string text) matches the selected headers as specified (case-sensitively) against theSpecpattern, e.g.'noreply@'. this matchmode can also be used to match IP version 6 patterns if the cidr-range uses multiples of 16-bit values, e.g.'[2a02:7a60:'.$β (script or program)Speccalls a script or program. the selected headers are passed to it, and it must return0to indicate a match and any other value to indicate no match. the script or program may specify arguments, but in addition two extra arguments get passed to it: the first is the name of a file containing the selected email headers, and the second is the name of the file containing the email's content. heads up though β the script or program does not use thePATHenvironment variable, so the path to the script or program (which may start with~/) must be specified. whenSpecstarts with the wordSHELL], the script or program is called as/bin/sh -c 'script/program [arguments]'. examples:
# call program in the user's bin dir. with 4 arguments
'~/bin/program arg1, arg2'
# call script in the user's bin dir. with 4 arguments
# via /bin/sh
'SHELL ~/bin/script arg1, arg2'
β¨ OPTIONS β¨
okay bestie, buckle up, this is the long one π but like, every single flag here is a real one, so no skimming, i'm watching π
short options β when they exist β are tucked inside parentheses right after their long-option twin. so if you see something like --config=path (-c), that means -c is the lil' nickname for --config=path. slay. π«Ά
several of these options take a location (a file that filtermail writes or reads). and locations have rules, bestie:
- if the location starts with
~/, that tilde gets replaced by the base directory that was given as filtermail's argument β¨ - otherwise, if the location does not start with a
/character, then filtermail prefixes it with the path of the directory that holds the configuration file πΈ
some of these options can also be specified in the configuration file (cf. section CONFIGURATION) β the ones that cannot are marked NO-CONFIG, and no cap, that label is not decorative. π
--accept([:HDRS:]path)β thepaththat receives accept-marked mail. mail that doesn't match any rules is accepted too, for the record. prepend:HDRS:topathand filtermail will log the mail'sFrom:andSubject:headers topathinstead. wanna completely ignore accept-marked mail? just leave this option out (although ignoring accept-marked mail is, like, probably not the move, ngl). examples (herefrankβ the name used in the example β gets replaced by the actual username):
--accept /var/mail/USER
--accept :HDRS:/var/mail/USER
--clsβ by default filtermail clears the terminal's screen (callingtput clear). if you'd rather it didn't clear the screen you can say--cls no. you can also set a default in the configuration file with
cls: no
in which case --cls yes on the command line will overrule whatever the config file said π
. heads up though: this option is only used when the --inspect option is specified.
- NO-CONFIG
--config=path(-c) β the path to the configuration file (default:~/etc/mailfilter/config). (see also section CONFIGURATION below π¦) - NO-CONFIG
--expire=date(-e) β no mail is read, bestie, none π but patterns whose date stamps are older thandateget removed from their files and stored in files with the extension.exp. (cf. section PATTERNS below.) use the formatyy-mm-ddwhen you're specifying the date. this option and--interactive(below) cannot both be specified β pick a lane. π example:
--date 23-01-31
- NO-CONFIG
--help(-h) β a summary of filtermail's usage is written tocoutand filtermail ends, returning0to the shell. iconic. β¨ --ignore([:HDRS:]path)β thepaththat receives ignore-marked mail. prepend:HDRS:topathto log the mail'sFrom:andSubject:headers topath. wanna completely ignore ignore-marked mail? omit this option entirely π«£ examples:
--ignore ~/Mail/ignore
--ignore :HDRS: ~/Mail/ignore
- NO-CONFIG
--inspect(-I) β when you specify this option, filtermail expects a received e-mail file on its standard input showing the domain name of the sender, its IP address, its country of origin, and the cidr-range containing the received IP address. like so:
from renxincj.com (unknown [104.223.188.228])
IP = `104.223.188.228β, Country: US, CIDR = 104.223.128.0/17
see section FILTERMAIL INSPECT for extra info π
- NO-CONFIG
--interactive(-i) β the matching result of matching patterns is interactively simulated (no mail is read, we're just vibing π§). this option and--expirecannot both be specified. --IP4-pattern=regex(-p) β by default, IP4 patterns are recognized using the regular expression pattern
\[((\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3}))\]
which matches addresses like [123.11.22.33] in specified headers. this format is commonly encountered in Received: headers. when you need different IP4-patterns recognized, this option is how you specify another regular expression for matching IP4-patterns in headers.
the regex you hand over is an extended regular expression, and the characters *, +, ?, ^, $, |, (, ), [, ], {, and } must be escaped when you're using them as literal characters. no skipping this, girlie, it will bite you. π€ perl-like escape sequences \b (word-boundary), \d (digit character), \s (white-space character), and \w (alpha-numeric character) are supported, and their capital variants represent their complementary sets.
regexes specified with this option must have at least five elements, addressable using indices 1..5:
- element 1 defines the complete IP4 pattern,
- element 2 defines the most significant (4th) octet (available in bits 24 thru 31 in the converted binary address),
- element 3 defines the 3th IP4 octet (available in bits 16 thru 23 in the converted binary address),
- element 4 defines the 2nd IP4 octet (available in bits 8 thru 15 in the converted binary address),
- element 5 defines the least significant (1st) IP4 octet (available in bits 0 thru 7 in the converted binary address).
--log=spec(-l) β specify the log-facility. by default no logging is used π Thespecargument can be the location of a file receiving the log messages. ifspecuses the formatfacility:level(facilityandlevelbeing the names defined in thesyslog(3)man-page, omitting theirLOG_prefixes) then the log messages are written bysyslog. examples:
--log log/log # uses log/log in the configuration
# fileβs directory
--log USER:NOTICE # uses syslog
--preambleβ merely verify that the basic options are correctly specified. no mail is read, but all options and configuration file specifications are processed. when the verification successfully ends, the message
Preamble successfully completed
is written to cout and filtermail returns 0. a lil' vibe check for your setup, we love to see it β¨
--received=content(-R) β before incoming mail reaches your computer, your (e.g.) organization's mail server may have processed the incoming mail, and it may be OK to inspect only theReceived:headers following that mail server'sReceived:header. the content of theReceived:header may (partially) be specified using this option, which can also be provided in the configuration file. e.g., if theReceived:header originating from your organization's mail server containsourorganization.orgthen the option-r ourorganization.orgcauses allReceived:headers to be ignored until theReceived:header containingourorganization.orghas been seen. if the incoming mail doesn't contain such aReceived:header then allReceived:headers are inspected. this option is only used when the--inspectoption is specified. π--rules=path(-r) β the location of the rules specification file (cf. section RULES below π).--spam [:HDRS:]pathβ thepaththat receives spam-marked mail. prepend:HDRS:topathto log the mail'sFrom:andSubject:headers topath. to completely ignore spam-marked mail, omit this option (bold choice but okay π). examples:
--spam ~/Mail/spam
--spam :HDRS: ~/Mail/spam
- NO-CONFIG
--syntaxβ perform a syntax check of the specified rules. encountered syntax errors are logged in the log-file. the begin and end of the syntax check is also logged in the log file. no mail is read from the standard input stream. basically a lint pass for your rules, and we're obsessed π§ - NO-CONFIG
--version(-v) β writes filtermail's version tocoutand ends, returning0. the classic.
π FILTERMAIL INSPECT π
when you specify the --inspect (-I) option, filtermail expects a received e-mail file at its standard input showing the domain name of the sender, its IP address, its country of origin, and the cidr-range containing the received IP address. like:
from renxincj.com (unknown [104.223.188.228])
IP = `104.223.188.228β, Country: US, CIDR = 104.223.128.0/17
mail handling programs (e.g., mutt(1)) let their users pipe an e-mail file to a program, so you can inspect the received e-mail from inside the mail handling program. slay. e.g., with mutt typing | shows the prompt
Pipe to command:
and assuming the filtermail program is available in the user's PATH environment variable, enter filtermail -I to pass the received e-mail to filtermail:
Pipe to command: filtermail -I
depending on the content of the Received: headers, filtermail's output shows the domain name of the sender, its IP address, its country of origin and the cidr-range containing the received IP address. e.g.,
from renxincj.com (unknown [104.223.188.228])
IP = `104.223.188.228β, Country: US, CIDR = 104.223.128.0/17
IP version 6 addresses are also inspected, producing output like
from mail.resoascijournal.info (s857e6ba3.fastvps-server.com \
[2a03:f480:2:8::3f])
IP = `2a03:f480:2:8::3fβ, Country: EE, CIDR = 2a03:f480:2::/48
if the received e-mail is considered conspicuous (e.g., spam or mail to ignore) then the cidr range could be added to a file like suspect.cidr. π© once more e-mails from the suspected cidr-range are received, the range could be added to, e.g., ~/etc/filtermail/spam/cidr or to ~/etc/filtermail/ignore/cidr, using a pattern line like
1 23-05-10 s β2a03:f480:2:β
when the option --cls is specified as yes (either as command-line option or in the configuration file) then the terminal screen will be cleared before showing --inspect's output. π§Ό when the option --received is specified, Received: headers appearing before the Received: header containing the content specified at the --received option are ignored. πΈ
π EXAMPLES π
okay bestie, real talk β here's filtermail actually doing its whole thing π«Ά
- make
filtermaila legit mail-handling program π
So incoming mail servers commonly define a directory where valid mail handling programs (or links to those programs) are listed. E.g., sendmail(8) uses the sendmail restricted shell (/etc/mail/smrsh) directory. If filtermail is installed in a standard user-accessible directory (e.g., /usr/bin) then the smrsh directory should contain the link
filtermail -> /usr/bin/filtermail
The way that once the filtermail program is recognized by the incoming mail server, users may filter incoming e-mail through filtermail using, e.g., a ~/.forward file. Such .forward files ignore empty lines and end-of-line comment (starting at #). Assuming a standard filtermail-configuration (cf. section CONFIGURATION π) and assuming that user frank's home-directory is /home/frank, then /home/frank/.forward should contain the following line:
"|/usr/bin/filtermail /home/USER"
Note the double quotes: they are required because filtermail is called with an argument. no quotes, no delivery, we don't make the rules π
- a configuration file β¨
The following configuration file specifies that the rules and log files are located in the configuration file's directory and defines paths for all three mail categories:
rules: rules
log: log/log
accept: /var/spool/mail/USER
spam: ~/Mail/spam
ignore: ~/Mail/ignore
# as illustration of a 'dont-expire:' specification:
#dont-expire: ./ignore/from
- rule specifications π
Filtering rules are defined in the file specified by the --rules option or in the rules: line of the configuration file. Note that the pattern files must start with ./. like, mandatory mandatory.
if From: ./ignore/from ignore
if Subject: ./spam/nolowercase spam
# inspect all Received... headers:
if Received+ ./spam/cidr spam
# a To: or Cc: header is required:
if To: ./match/noto and Cc: ./match/noto ignore
The final rule uses the pattern in ./match/tocc (shown below) specifying the any character (.) regular expression: if the To: header is empty (which is also true if there is no To: header, then the not To: condition matches. The same holds true for the second condition. So if neither condition matches there is neither a To: nor a Cc: header, in which case the e-mail is sent to the spam destination. Also note that, according to De Morgan's rule, a not X and not Y rule is identical to an X or Y rule. math ate, fr fr π§
- pattern specifications πΈ
# the ./spam/nolowercase pattern:
1 23-05-10 not p '[a-z]'
# e.g., the ./match/noto as used in the above example
# requiring either a To: or a Cc: header:
1 23-05-10 p not '.'
π FILES π
By default the configuration file is expected in the subdirectory etc/filtermail of the directory specified as filtermail's argument. yes, it goes looking for it there all on its own, main character energy π¦
π― SEE ALSO π―
mutt(1), pattern(3bobcat), regcomp(3), sendmail(8), syslog(3), tput(1), whois(1)
π BUGS π
None reported. (iconic behavior honestly, we love a clean bug list π)
π AUTHOR π
Frank B. Brokken (f.b.brokken@rug.nl).
and that's the whole page bestie, you made it to the end π this absolute gem is filtermail_1.07.01, dated 2023-2025, and yes it really is filtermail(1). go filter some mail, you've got this π
β¨π¬
π¦ where this page comes from
- ubuntu stonking: filtermail 1.07.01-1build1 Β· GPL-3.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms