MailScript Language Reference

MailScript is a powerful email filtering and automation scripting language based on Starlark (Python-like syntax). Create sophisticated email processing rules with 51 built-in functions for filtering, routing, security, and network validation.

Table of Contents

Overview

Scripts are executed for each incoming email message. You can define an evaluate() function as the main entry point, or write code at the root level. The script has access to email headers, body content, and metadata through built-in functions.

Language Features

MailScript is built on Starlark, a Python-like language with support for:

Example: Lists and Loops

spam_keywords = ["viagra", "cialis", "lottery"]

for keyword in spam_keywords:
    if search_body(keyword):
        quarantine()
        return

Example: Dictionaries

spam_scores = {
    "viagra": 10.0,
    "lottery": 8.5,
    "winner": 7.0
}

for keyword, score in spam_scores.items():
    if search_body(keyword):
        add_header("X-Spam-Match", keyword)
        add_header("X-Spam-Score", str(score))

Message Actions15 functions

accept()

Accept and deliver the message to the inbox.

accept()
discard()

Silently drop the message without delivering it.

discard()
drop()

Forcefully drop the message without processing.

drop()
bounce()

Bounce the message back to the sender.

if getspamscore() > 9.0:
    bounce()
quarantine()

Move the message to quarantine for manual review.

if getvirusstatus() == "infected":
    quarantine()
fileinto(folder)

Move the message to a specific folder.

folder (string) - Name of the folder
fileinto("Spam")
fileinto("Archive/2024")
add_to_next_digest()

Add the message to the next digest email.

Returns True if successful
if add_to_next_digest():
    log_entry("Added to digest")
auto_reply(text)

Send an automated reply to the sender.

text (string) - Reply message text
auto_reply("Thank you for your email. We will respond within 24 hours.")
reply_with_smtp_error(code)

Reply with a specific SMTP error code.

code (int) - SMTP error code (550, 554, 421, etc.)
reply_with_smtp_error(550)  # Mailbox unavailable
reply_with_smtp_dsn(dsn)

Reply with an SMTP Delivery Status Notification.

dsn (string) - DSN string (e.g., "5.7.1", "5.1.1")
reply_with_smtp_dsn("5.7.1")  # Delivery not authorized

Header Operations

get_header(name)

Get the value of an email header.

name (string) - Header name ("From", "Subject", "To")
Returns string value of the header
sender = get_header("From")
subject = get_header("Subject")
add_header(name, value)

Add a custom header to the message.

name (string) - Header name
value (string) - Header value
add_header("X-MailScript", "processed")
add_header("X-Spam-Score", "7.5")
search_body(text)

Search for text in the message body (literal match).

text (string) - Text to search for
Returns True if found, False otherwise
if search_body("viagra"):
    fileinto("Spam")
regex_match(pattern, text)

Test if text matches a regular expression pattern.

pattern (string) - Regular expression pattern
text (string) - Text to test
Returns True if matches, False otherwise
sender = get_header("From")
if regex_match(".*@trusted\\.com$", sender):
    accept()

Message Metadata7 functions

getspamscore()

Get the spam score of the message (0.0 to 10.0).

Returns float value
score = getspamscore()
if score > 7.0:
    fileinto("Spam")
getvirusstatus()

Get the virus scan status of the message.

Returns "clean", "infected", or "unknown"
if getvirusstatus() == "infected":
    quarantine()
getmimetype()

Get the MIME type of the message.

Returns string (e.g., "text/plain", "multipart/mixed")
mime = getmimetype()
if mime == "application/pdf":
    add_header("X-Has-PDF", "true")
body_size()

Get the size of the email body in bytes.

Returns integer (bytes)
bs = body_size()
if bs > 10485760:  # 10MB
    reply_with_smtp_error(552)
    drop()
header_size()

Get the size of the email headers in bytes.

Returns integer (bytes)
hs = header_size()
if hs > 102400:  # 100KB
    log_entry("Large headers detected")
num_envelope()

Get the number of envelope senders.

Returns integer
n = num_envelope()
if n > 10:
    log_entry("Suspicious: multiple envelope senders")
    quarantine()

Routing Actions

divert_to(email_address)

Redirect the message to a different email address.

email_address (string) - Target email
if regex_match(".*(invoice|payment).*", subject):
    divert_to("accounting@example.com")
screen_to(email_address)

Send a copy of the message to another address for screening/review.

email_address (string) - Screening address
if getspamscore() > 5.0:
    screen_to("admin@example.com")
force_second_pass(mailserver)

Route the message to another mail server for additional processing.

mailserver (string) - Mail server hostname
force_second_pass("priority-server.example.com")

Security Controls

skip_malware_check(sender)

Bypass malware scanning for messages from a specific sender.

sender (string) - Sender email address
if regex_match(".*@trusted\\.com$", sender):
    skip_malware_check(sender)
skip_spam_check(sender)

Bypass spam filtering for messages from a specific sender.

sender (string) - Sender email address
skip_spam_check("newsletter@company.com")
skip_whitelist_check(ip)

Bypass whitelist checking for a specific IP address.

ip (string) - IP address
skip_whitelist_check("192.168.1.100")

Data Loss Prevention

set_dlp(mode, target)

Set Data Loss Prevention policy for the message.

mode (string) - Policy mode
target (string) - Target scope ("user", "domain")
if search_body("confidential"):
    set_dlp("always", "domain")
skip_dlp(mode, target)

Skip DLP checks for the message.

mode (string) - Policy mode
target (string) - Target scope
skip_dlp("sometimes", "user")

DNS & Network Functions13 functions

get_sender_ip()

Get the IP address of the message sender.

Returns string (IP address)
sender_ip = get_sender_ip()
log_entry("Message from IP: " + sender_ip)
get_sender_domain()

Get the domain of the message sender.

Returns string (domain name)
sender_domain = get_sender_domain()
add_header("X-Sender-Domain", sender_domain)
dns_check(domain)

Check if a domain has valid DNS records.

domain (string) - Domain to check
Returns True if DNS resolves
if not dns_check(sender_domain):
    reply_with_smtp_error(550)
    drop()
rbl_check(ip, rbl_server)

Check if an IP address is listed in a Real-time Blackhole List (RBL).

ip (string) - IP address to check
rbl_server (string, optional) - Specific RBL server
Returns True if listed
sender_ip = get_sender_ip()
if rbl_check(sender_ip, "zen.spamhaus.org"):
    quarantine()
valid_mx(domain)

Check if a domain has valid MX (Mail Exchange) records.

domain (string) - Domain to check
Returns True if valid MX records exist
if not valid_mx(sender_domain):
    reply_with_smtp_error(550)
    bounce()
get_mx_records(domain)

Get all MX records for a domain.

domain (string) - Domain to lookup
Returns list of MX record strings
mx_records = get_mx_records(sender_domain)
for mx in mx_records:
    log_entry("MX: " + mx)

Received Headers Analysis

check_received_header(level)

Get a specific Received header at the given level (0 = most recent hop).

level (int) - Header level (0-based)
Returns string (Received header content)
# Check third hop for spam relay patterns
third_hop = check_received_header(2)
if "suspicious-relay.com" in third_hop:
    quarantine()
get_received_headers()

Get all Received headers as a list (most recent to oldest).

Returns list of Received header strings
received = get_received_headers()
num_hops = len(received)

if num_hops > 10:
    log_entry("Suspicious: " + str(num_hops) + " mail hops")
    quarantine()

Complete Examples

Example 1: Comprehensive Spam Filter

def evaluate():
    sender = get_header("From")
    sender_ip = get_sender_ip()
    sender_domain = get_sender_domain()

    # DNS validation
    if not dns_check(sender_domain):
        reply_with_smtp_error(550)
        drop()
        return

    # RBL check
    if rbl_check(sender_ip, "zen.spamhaus.org"):
        quarantine()
        return

    # MX validation
    if not valid_mx(sender_domain):
        bounce()
        return

    # Spam score check
    if getspamscore() > 8.0:
        quarantine()
        return

    # Virus check
    if getvirusstatus() == "infected":
        quarantine()
        return

    # Accept clean mail
    accept()

Example 2: Smart Routing

def evaluate():
    subject = get_header("Subject")
    sender = get_header("From")

    # Route invoices to accounting
    if regex_match(".*(invoice|payment|billing).*", subject.lower()):
        divert_to("accounting@example.com")
        return

    # Trusted senders bypass checks
    if regex_match(".*@trusted\\.com$", sender):
        skip_spam_check(sender)
        skip_malware_check(sender)
        accept()
        return

    # Default processing
    accept()

Example 3: Advanced Network Validation

def evaluate():
    sender_domain = get_sender_domain()
    sender_ip = get_sender_ip()

    # Get all received headers
    received = get_received_headers()

    # Too many hops = suspicious
    if len(received) > 10:
        log_entry("Suspicious relay chain: " + str(len(received)) + " hops")
        quarantine()
        return

    # Check for known spam relays
    for hop in received:
        if "known-spammer.com" in hop:
            drop()
            return

    # Validate MX records and check for RBL
    if valid_mx(sender_domain):
        mx_records = get_mx_records(sender_domain)
        for mx in mx_records:
            log_entry("MX record: " + mx)
    else:
        bounce()
        return

    # Check if MX servers are in RBL
    if mx_in_rbl(sender_domain, "zen.spamhaus.org"):
        add_header("X-MX-Blacklisted", "true")
        quarantine()
        return

    accept()

Security Note: Scripts run in a sandboxed Starlark environment with no file system or network access. Regular expressions are evaluated with timeouts to prevent ReDoS attacks.