Skip to content

Instantly share code, notes, and snippets.

@mdecimus
Last active June 18, 2024 12:39
Show Gist options
  • Save mdecimus/22d52c47f013d35c82e074ce45fbf0f7 to your computer and use it in GitHub Desktop.
Save mdecimus/22d52c47f013d35c82e074ce45fbf0f7 to your computer and use it in GitHub Desktop.
jMilter - JSON mail filtering and manipulation protocol

Sample jMilter request for the DATA stage:

{
    "context": {
        "stage": "DATA",
        "sasl": {
            "login": "user",
            "method": "plain"
        },
        "client": {
            "ip": "192.168.1.1",
            "port": 34567,
            "ptr": "mail.example.com",
            "ehlo": "mail.example.com",
            "activeConnections": 1
        },
        "tls": {
            "version": "1.3",
            "cipher": "TLS_AES_256_GCM_SHA384",
            "cipherBits": 256,
            "certIssuer": "Let's Encrypt",
            "certSubject": "mail.example.com"
        },
        "server": {
            "name": "Stalwart Mail Server",
            "port": 25,
            "ip": "192.168.2.2"
        },
        "queue": {
            "id": "1234567890"
        },
        "protocol": {
            "version": "1.0"
        }
    },
    "envelope": {
        "from": {
            "address": "[email protected]",
            "parameters": {
                "size": 12345
            }
        },
        "to": [
            {
                "address": "[email protected]",
                "parameters": {
                    "orcpt": "rfc822; [email protected]"
                }
            },
            {
                "address": "[email protected]",
                "parameters": null
            }
        ]
    },
    "message": {
        "headers": [
            [
                "From",
                "John Doe <[email protected]>"
            ],
            [
                "To",
                "Bill <[email protected]>, Jane <[email protected]>"
            ],
            [
                "Subject",
                "Hello, World!"
            ]
        ],
        "serverHeaders": [
            [
                "Received",
                "from mail.example.com (mail.example.com [192.168.1.1]) by mail.foobar.com (Stalwart Mail Server) with ESMTPS id 1234567890"
            ]
        ],
        "contents": "Hello, World!\r\n",
        "size": 12345
    }
}

Notes:

  • HTTP POST requests are used with authentication being optional.
  • Message bodies are provided raw as content filters usually need to analyse the MIME structure as well.
  • jMilters can be called from any stage of the SMTP transaction so most of these fields are optional.
  • message.headers[][] is an array of arrays to keep the JSON representation compact. This can be changed to an array of objects if needed.

Sample jMilter response:

{
    "action": "accept",
    "response": {
        "status": 250,
        "enhancedStatus": "2.0.0",
        "message": "Message accepted",
        "disconnect": false
    },
    "modifications": [
        {
            "type": "changeFrom",
            "value": "[email protected]",
            "parameters": {
                "size": 54321
            }
        },
        {
            "type": "addRcpt",
            "value": "[email protected]",
            "parameters": null
        },
        {
            "type": "deleteRcpt",
            "value": "[email protected]"
        },
        {
            "type": "replaceBody",
            "value": "This is the new body\r\n"
        },
        {
            "type": "addHeader",
            "name": "X-Spam-Status",
            "value": "No"
        },
        {
            "type": "insertHeader",
            "index": 1,
            "name": "X-Filtered-By",
            "value": "Custom Filter v1.1",
        },
        {
            "type": "changeHeader",
            "index": 4,
            "name": "Subject",
            "value": "This is the new subject",
        },
        {
            "type": "deleteHeader",
            "index": 1,
            "name": "X-Mailer",
        }
    ]
}

Notes:

  • response is optional and allows the filter to customise the SMTP response.
  • Possible actions are:
    • accept
    • discard
    • reject
    • quarantine
@mdecimus
Copy link
Author

mdecimus commented Jun 18, 2024

To me, this sounds like queue placement? quarantine/defer/incoming.

Yes, it moves the message to the quarantine queue. Quarantining messages is not something that Stalwart supports for it is part of the Milter protocol.

But then why are both "changeHeader" and "quarantine" types, for example?

Milter includes quarantine as one of the possible modifications, I guess this is to allow combining quarantining with any of the possible actions. Perhaps instead of having it as an action we can add an optional quarantine boolean to the response, something like this:

{
    "action": "accept",
    "quarantine": true,
    "modifications": []
}

Edit: Also quarantine could be an action with some additional parameters to either accept, reject, etc.

replaceContents make sense.

Just updated the schema. Also renamed body to contents.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment