The filter is located in the user’s /.hato/filter file. It is evaluated as Scheme script in a restricted environment with various procedures acting on a default (current-mail) object. The final result should be either a string or list or strings indicating mail destinations.
If the final result is false or undefined the mail is delivered to (default-folder). Likewise if any errors occur they will be caught, logged, and the mail delivered to (default-folder).
You may return a result immediately with the escape continuation bound to ’return’.
During execution of the filter anything written to (current-output-port) is logged to /.hato/filter.log.
If the user’s /.hato/filter is not found, then processing proceeds to the user’s /.forward. This may either be an /etc/aliases list of addresses to forward to, or an RFC 3028 Sieve filter script. To enable the latter, first line should be a # comment including the word “Sieve” (this is compatible with the Exim implementation). See http://www.faqs.org/rfcs/rfc3028.html for more details.
Currently the filter environment includes all of R5RS (except LOAD, TRANSCRIPT-ON/OFF and the default environments), and is specifically case-insensitive, even if the host Scheme implementation defaults to case-sensitive. In addition, the following utilities are provided:
(current-mail)
- current mail object, implicit in most procedures
(default-folder)
- default mail folder is not handled in filter
(spam-probability)
- probability from builtin filter
(is-spam?)
- #t iff probability is above default threshold
(is-duplicate?)
- #t iff we’ve already received this Message-Id
(domain-key-verify [text])
- #f on invalid keys (see domain-keys.scm)
(headers)
- mail headers as an alist
(header <name>)
- fetch header by case-insensitive <name>
(Urls)
- all urls found in message header or body
(Emails)
- all emails found in message header or body
(Ips)
- all ip addresses found in message header or body
(Date)
- date of mail from Date or Envelope, default now
(From)
- sender of mail, from Date or Envelope
(To)
- all to addresses (parsed, including the addr only)
(Cc)
- all cc recipients
(To/Cc)
- all mail recipients
(Subject)
- message subject
(Mailer)
- User-Agent or X-Mailer
(Message-Id)
- message ID
(Content-Length)
- size of mail in bytes
(From? [addrs ...])
- true if the mail was sent From any of the addrs
(To? [addrs ...])
- true if the mail was sent To any of the addrs
(Cc? [addrs ...])
- true if the mail was Cc’ed to any of the addrs
(To/Cc? [addrs ...])
- true if the mail was sent or Cc’ed to any of the addrs
(open-db file [read-only?])
(close-db db)
(db-ref db key [default])
(db-set! db key val)
(db-delete! db key)
(white-list [string])
- add to white-list
(black-list [string])
- add to black-list
(white-list? [string])
- check if in white-list
(black-list? [string])
- check if in black-list
(auto-list [rename [my-addresses ...]])
- automatically file into separate mailing-list archives by consulting the List-Id and X-Mailing-List headers
(discard [reason])
- discard the mail
(reject [reason])
- reject the mail at the SMTP protocol level
(refuse [reason])
- refuse the mail at the SMTP protocol level
;; initial duplicate & spam checks (cond ((is-duplicate?) (print "discarding duplicate: " (Message-Id)) (discard)) ((not (white-list?)) (cond ((not (domain-key-verify)) (refuse)) ((> (spam-probability) 0.90) (refuse)) ((> (spam-probability) 0.60) (return "spam")) (else (white-list))))) ;; manual filtering & folder handling, etc. (cond ((To? "my-mail-list@nosuchdomain.comm") "my-mail-list") (else (auto-list)))
Note in the above filter the To?
test is redundant if the
List-Id header uses my-mail-list as the local part. If you’re using
auto-list
then the only reason to manually filter lists is if
you want to specify alternate mailbox names.