9. Filter editor

Filter rules get applied to messages on sent or receive. Each filter has a match condition and a list of ordered actions that are automatically applied server-side when the condition is met. Condition expressions yield a boolean result and are written much like in a programming language, with operators, function calls, the logical connectors AND,OR,NOT. Typical actions consist of tagging the message, setting a priority, a status, redirecting or discarding it automatically.

The filter's direction can be Incoming mail to test the rule only against incoming messages (imported by manitou-mdx), Outgoing mail against messages that are written and sent with Manitou-Mail, Both for both incoming and outgoing, or None to disable the filter altogether.

The user interface has an editor for conditions and actions, launched by the File->Filters menu command. Below is a screenshot with some sample rules defined:

Filter rule editor

9.1. Expressions syntax

9.1.1. Literal constants

Numbers must be positive integers in base 10. When a number is immediately followed by the letter K, it is multiplied by 1024 (for kilobytes). When it is immediately followed by the letter M, it is multiplied by 1024*1024 (for megabytes), and when followed by G, it is multiplied by 1024*1024*1024 (for gigabytes). The letter after the number can be uppercase or lowercase.

Strings must be enclosed between double quotes or single quotes. If the first character is a single quote, double quotes can be used in the string as normal characters (example: 'Result of "command"'). Conversely, if the first character is a double quote, single quotes can be used normally inside the string (example: "Scarlett O'Hara")

A string may also contain the enclosing character itself. In this case, the entire string must be started by an backslash before the single or double quote, and the non-terminating occurrence of the enclosing character inside the string must be preceded by a backslash. In addition, to represent a backslash itself in such a string, the backslash character must be doubled. For example, a regexp that matches single or double quotes could be written as: \"[\"\']"

9.1.2. Operators

Table V.2. Operators for filter expressions

OperatorExampleDescription
!!condition("urgent")Logical inverse of the operand. Equivalent to not.
=header("Precedence")="bulk"Test the case insensitive equality of two strings. Equivalent to is
isheader("Precedence") is "bulk"Test the case insensitive equality of two strings. Equivalent to the equal sign.
isnotto isnot "me@example.com"This is the logical inverse of the is operator.
eqheader("Precedence") eq "bulk"Test the exact equality of two strings (case sensitive).
neheader("Precedence") ne "bulk"This is the logical inverse of the eq operator (case-sensitive equality of strings).
contain
contains
header("from") contains "@domain.net"True if the left operand contains the right operand, case insensitive.
!=now("month")!=12Test the inequality of two numeric values.
==date("weekday")==1Test the equality of two numeric values.
<
<=
>
>=
age("days")>=2Compare two numeric values, testing for lower, lower or equal, greater, greater or equal.
=~header("subject") =~ "^\[URGENT\]"True if the left operand matches the regular expression in the right operand, case insensitive.
!~header("subject") !~ "^\[URGENT\]"True if the left operand does not match the regular expression in the right operand, case insensitive.
regmatchesheader("subject") regmatches "^\[URGENT\]"True is the left operand against the regular expression matches the right operand, case sensitive.
andfrom is "a@domain.com" AND to is "b@domain.com"True if both the left operand and right operand evaluate to true.
orfrom is "a@domain.com" OR from is "b@domain.com"True if either of the left operand or right operand evaluate to true.

Arbitrarily complex expressions can be written by combining conditions and operators and nested parentheses. Example:

(pg_gen OR pg_interf) AND NOT (header("From") contains "@mydomain.tld")

All the expressions that have at least one action connected to them are evaluated, unless the "Stop filters" action gets triggered.

9.2. Built-in functions

Expressions may call built-in functions that implicitly refer to the message being filtered. Functions provide information about the messages, the context and meta-data, but they're not allowed to modify anything. On the other hand, filter actions may modify some message properties, such as the priority or certain header fields. In this case, functions that are called subsequently work on the modified data. Another way of expressing this is that the effects of filters will cascade onto the filters.

Table V.3. Built-in filter functions

Function nameReturn typeArgumentsDescription
agenumber A time unit as a string, which must be one of "minutes", "hours" or "days". Return the time elapsed since the date and time indicated in the Date field of the message, as a number of minutes, hours or days depending on the argument. If the date field cannot be parsed, the result is null.
bodystringNoneReturn the entire body of the message.
ccstringNoneReturn a comma-separated list of addresses extracted from the Cc (carbon copy) field. If the Cc field is "John Doe" <john.doe@email.tld>, the cc function will return john.doe@email.tld (whereas header("Cc") would return the entire field).
conditionbooleanCondition nameEvaluate the named condition and return its result. The name refers to another filter entry.
datebooleanDate fieldReturn the date and time of the message in the local time zone. The possible arguments are the same than for the now() function.
date_utcbooleanDate fieldIdentical to the date() function, except that the date and time is expressed in UTC time instead of the local time zone.
fromstringNoneReturn the sender's address extracted from the From field. In the unlikely event that there are several addresses in this field, return a comma-separated list of these addresses.
headerstringField nameReturn the decoded value of the header field. The line is unfolded and non US-ASCII characters encoded with the RFC-2047 rules have been decoded and converted to unicode. Use rawheader() to access the non-decoded version.
headersstringNoneReturn the decoded value of the entire message header as one big string with consecutive fields separated by a newline character. The lines are unfolded and non US-ASCII characters encoded with the RFC-2047 rules have been decoded and converted to unicode. This function should be used only in the rare cases where it's not adequate to target a specific field with the header() function.
identitystringnoneReturn the email address from our identities that is associated with the message. For an outgoing message, that would normally be the address of the From field. For an incoming message, it is the mail address that is associated with the spool directory from wich the mailfile has been picked up. These associations are declared in manitou-mdx configuration file.
nowstringDate fieldReturn the current date and time in the local timezone. The possible arguments and corresponding return values are:
  • "hour": the current hour with 2 digits, "00".."23"
  • "minute": the current minute with 2 digits, "00".."59"
  • "second": the current second with 2 digits, "00".."59"
  • "day": the day of the month with 2 digits, "01".."31"
  • "month": the month with 2 digits, "01".."12"
  • "year": the year with 4 digits
  • "weekday": the index of the day in the week with 1 digit "0".."6" (0 being sunday)
  • "date": the current date expressed as YYYY-MM-DD
  • "time": the current time expressed as HH:MM:SS
The result of now() refers to the beginning of the filter evaluation, so that it doesn't change during the evaluation itself.
now_utcstringDate fieldThis is identical to now(),except that it returns the date in UTC time instead of the local timezone.
rawheaderstringField nameReturn the non-decoded value of the header field, possibly with quoted-printable and base64 contents according to RFC2047. The line is unfolded.Generally the headerfunction should be used instead.
rawheadersstringNoneReturn the non-decoded value of the entire message header as one big string with consecutive fields separated by a newline character, possibly with quoted-printable and base64 contents according to RFC2047. Generally the headersfunction should be used instead.

9.3. Filter actions

The action panel refers to the currently selected condition. It is disabled when no condition is selected. To add an action, select (New action) in the actions list. To remove the currently selected action, use the Del key.

The actions that are currently supported are:

  • Assign tag: assign a tag to the matching messages. Child tags are expressed flattened as parent_tag->child_tag->grandchild_tag->...

  • Set status: the argument is a combination of letters: 'R' for read,'A' for archive, all separated by + signs.

  • Remove header: discard the specified header field. If the field is present multiple times, all its occurrences will be removed.

  • Set header: set a header field. If the field is already present, the new value will replace the old one in all occurrences of the field, otherwise the field will be added.

  • Set priority: assign a priority to the matching messages, either as an increment, with the +=X syntax or as an absolute value, with the =X syntax. The priority is an integer number ranging from -32768 to +32767.

  • Redirect: the argument is an email address to which the matching message is immediately resent.

  • Delete: dicard the message or put it into the trashcan. The latter is equivalent to having the trashed status.

  • Stop filters: this is a control-action that instructs the filtering the system that it shouldn't evaluate any more filter rule for the current message.

  • Set identity: assign a different identity to the message. Each incoming message is associated by manitou-mdx to an identity defined in Preferences/Identities, and each outgoing message is also associated to a sender's identity chosen in the mail composer. The purpose of this action is to override this identity. One practical use is that when replying to an incoming message with an updated identity, the new identity will automatically be pre-selected as the sender of the reply.

9.4. Use cases examples

9.4.1. High priority routing during non-business hours

Consider alert e-mails sent by a Nagios monitoring system. If they're received outside of business hours, we want to transfer them to some other mail account, for example one that is routed to a mobile phone.

Condition name: alert
Expression: from is "nagios@domain.tld" AND subject contains "** PROBLEM Service Alert"
Actions: none

Condition name: business hours
Expression: (now("hour") >= 9) AND (now("hour") < 18)
Actions: none

Condition name: (not necessary)
Expression: condition("alert") AND (NOT condition("business hours"))
Actions: redirect to mobile's email

Condition name: (not necessary)
Expression: condition("alert") AND condition("business hours")
Actions: set priority to +10

9.4.2. Ignoring undesirable Reply-To fields

Normally, the presence of a Reply-To field suggests that a reply should go only to the addresses designated by the field, there-by ignoring the From and Cc fields. But this feature is sometimes misused, either by mailing-lists software or by senders who set up their mail account with a Reply-To field always set.

The filtering system provides an action that removes a specific header field. That can be used against these undesirable Reply-To fields. Example:

Expression: from is "specific-email@domain.tld" AND header("reply-to") is "specific-email@domain.tld"
Actions: Remove header field Reply-To

9.4.3. Filtering on dates

The current day is the first day of the month: now("day")="01" or now("day")==1 (with implicit conversion to numeric).
The year is 2011: now("year")="2011".
The day is exactly the November 17, 2011: now("date")="2011-11-17"
The sender's date on the message is November 17, 2011: date("date")="2011-11-17"