Package API: kilter.service

High level, asynchronous framework for writing mail filters

Kilter is a framework for writing mail filters (known as “milters”) compatible with Sendmail and Postfix MTAs. Unlike many previous milter implementations in Python it is not simply bindings to the libmilter library (originally from the Sendmail project). The framework aims to provide Pythonic interfaces for implementing filters, including leveraging coroutines instead of libmilter’s callback-style interface.

class After(subject: Header)[source]

Bases: Position

Indicates a relative position following a subject Header in a header list

See HeadersAccessor.insert.

subject: Header
class Before(subject: Header)[source]

Bases: Position

Indicates a relative position preceding a subject Header in a header list

See HeadersAccessor.insert.

subject: Header
class Runner(*filters: Filter)[source]

Bases: object

A filter runner that coordinates passing data between a stream and multiple filters

Instances can be used as handlers that can be passed to Listener.serve() or used with any ByteStream.

class Session(connmsg: Connect, sender: AsyncGenerator[None, kilter.service.session.EditMessage], broadcast: Optional[Broadcast[kilter.service.session.EventMessage]] = None)[source]

Bases: object

The kernel of a filter, providing an API for filters to access messages from an MTA

host: str

A hostname from a reverse address lookup performed when a client connects

If no name is found this value defaults to the standard presentation format for Session.address surrounded by “[” and “]”, e.g. “[192.0.2.100]”

address: ipaddress.IPv4Address | ipaddress.IPv6Address | pathlib.Path | None

The address of the connected client, or None if unknown

port: int

The port of the connected client if applicable, or 0 otherwise

macros: dict[str, str]

A mapping of string replacements sent by the MTA

See smfi_getsymval from libmilter for more information.

Warning

The current implementation is very naïve and does not behave exactly like libmilter, nor is it very robust. It will definitely change in the future.

headers: HeadersAccessor

A HeadersAccessor object for accessing and modifying the message header fields

body: BodyAccessor

A BodyAccessor object for accessing and modifying the message body

async deliver(message: kilter.service.session.EventMessage) type[kilter.protocol.messages.Continue] | type[kilter.protocol.messages.Skip][source]

Deliver a message (or its contents) to a task waiting for it

async helo() str[source]

Wait for a HELO/EHLO message and return the client’s claimed hostname

async envelope_from() str[source]

Wait for a MAIL command message and return the sender identity

Note that if extensions arguments are wanted, users should use Session.extension() instead with a name of "MAIL".

async envelope_recipients() AsyncIterator[str][source]

Wait for RCPT command messages and iteratively yield the recipients’ identities

Note that if extensions arguments are wanted, users should use Session.extension() instead with a name of "RCPT".

async extension(name: str) memoryview[source]

Wait for the named command extension and return the raw command for processing

async change_sender(sender: str, args: str = '') None[source]

Move onto the Phase.POST phase and instruct the MTA to change the sender address

async add_recipient(recipient: str, args: str = '') None[source]

Move onto the Phase.POST phase and instruct the MTA to add a new recipient address

async remove_recipient(recipient: str) None[source]

Move onto the Phase.POST phase and instruct the MTA to remove a recipient address

Submodules