TclMilter is a package for Tcl (written in C) that implements an interface to SendMail's Milter (Mail Filter) API for developing custom scripted message rewriting and spam filtering processes. A thread-enabled Tcl build is required due to Milter's threading requirements. However, you can only run, configure, or register callbacks for a milter from the first thread/interpreter the package is loaded into. If you need to be able to stop the milter from within Tcl, you can load the package in another thread to do so. Only the "milter stop" command will function in secondary threads.
package require Tcl 8.1
package require TclMilter ?1.0?
TclMilter is a package for Tcl (written in C) that implements an interface to SendMail's Milter (Mail Filter) API for developing custom scripted message rewriting and spam filtering processes. A thread-enabled Tcl build is required due to Milter's threading requirements. However, you can only run, configure, or register callbacks for a milter from the first thread/interpreter the package is loaded into. If you need to be able to stop the milter from within Tcl, you can load the package in another thread to do so. Only the "milter stop" command will function in secondary threads.
TclMilter follows libmilter's APIs very closely, with the following exceptions for readability and flexibility:
Callbacks are invoked by TclMilter at various stages of message processing in order to provide a filter with all of the information about the MTA connection and message being sent. These callbacks may return one of five values to indicate to SendMail whether to continue processing the message or whether to reject it for some reason.
Configure Milter session parameters. Given no options, returns a list of option/value pairs indicating current milter configuration. Given an option and no values, returns the current value of the specified option. Otherwise, each option is set to the given value.
Specifies the type of connection to use for communicating with SendMail, as well as the appropriate parameters for the connection type. It must be set before calling milter main if at all. Per libmilter's smfi_setconn documentation, the format is "proto:address":
The default is "unix:/var/run/tclmilter.sock" - a pipe socket using the file "/var/run/tclmilter.sock".
Register a callback for TclMilter to invoke at the specified stage on mail processing. Callbacks are supported corresponding to each of those provided by libmilter its self. Unlike libmilter, which must have callbacks specified by the call to smfi_main(), callbacks for TclMilter may be registered or unregistered at any time. The callback argument specifies the name of the procedure for TclMilter to invoke for the specified event. If this argument is not provided, the command will return the name of the currently registered callback procedure for the specified event.
The type and arguments that the callback receives depend on the event, although every callback is provided with a unique context ID that can be used to manipulate the message being processed. Supported event types are as follows:
Arranges for proc connectProc to be called when a connection is established to sendmail, before the exchange of HELO greetings. The callback is appended with three arguments: the context command, the hostname and a list containing a dictionary of information from the sockaddr stucture (e.g., address and port).
proc connectProc { context hostname addrInfo } { ... }
Arranges for proc heloProc to be called when the client sends its HELO greeting command. The command is appended with two arguments: the context and the hostname as provided in the HELO command.
proc heloProc { context helohost } { ... }
Arranges for proc envfromProc to be called when the client sends the MAIL FROM command. The command is appended with two elements: the context command and a list of all of the addresses specified in the MAIL FROM envelope.
proc envfromProc { context fromList } { ... }
Arranges for proc envrcptProc to be called when the client sends the RCPT TO command. The command is appended with two elements: teh context command and a list of all of the addresses specified in the RCPT TO envelope.
proc envrcptProc { context rcptList } { ... }
Arranges for proc headerProc to be called for each header sent by the client. The command is appended with three elements: the context command, the header name and the contents of the header.
proc headerProc { context name content } { ... }
Arranges for proc eohProc to be called once all headers have been sent and handled by the milter's "header" callback, before the message body is sent. The command is appended with one argument: the context command.
proc eohProc { context } { ... }
Arranges for proc bodyProc to be called for each block of data in the message body. The command is appended with two elements: the context command and the body text. Each message has only one body, but this body may be sent in multiple blocks. If the milter intends to do content filtering on the message body, it is recommended to use context setpriv to build and store the complete message body and perform content filtering in the eom callback.
proc bodyProc { context block } { ... }
Arranges for proc eomProc to be called once once all message information has been received, including the entire message body, and processed by the milter. All message rewriting, such as adding and removing headers or changing the body, must be performed within this callback. The command is appended with one element: the context command.
proc eomProc { context } { ... }
Arranges for proc abortProc to be called if message transmission is aborted, such as due to a dropped connection. This callback is not invoked if message transmission is completed. The command is appended with one element: the context command.
proc abortProc { context } { ... }
Arranges for proc closeProc to be called when the client connection has been closed. This callback is not invoked if message transmission is aborted. The command is appended with one element: the context command.
proc closeProc { context } { ... }
The following context functions may be invoked from any callback to get or store information about the message context or sendmail configuration. Each context has a hash table associated with it, and a set of accessor functions that work similar to Tcl's own set command, allowing the milter to associate data with each message that can be passed around between callbacks and automatically garbage collected when the context is deleted.
The following context functions may be invoked ONLY from the eom callback to modify message contents. The result of invoking these commands in other callbacks is undefined.
The following context functions may be invoked ONLY from the eom callback. The result of invoking these commands in other callbacks is undefined.
#!/usr/local/bin/tclsh # # A simple email address-based blacklist milter # package require Tcl 8.4 package require TclMilter 1.0 # Configuration set blacklist { friend@public.com spammer@spamdomain.com } milter configure -name Blacklist milter configure -connection {unix:/var/run/blmilter.sock} # Check to see if an address is on the blacklist proc is_blacklisted { address } { set address [string trim $address <>] if {[lsearch -exact $::blacklist $address] != -1} { return 1 } return 0 } # Callbacks proc bl_header { context name data } { if {($name == "From") && [is_blacklisted $data]} { return SMFIS_REJECT } return SMFIS_CONTINUE } proc bl_envfrom { context addressList } { foreach address $addressList { if {[is_blacklisted $address]} { return SMFIS_REJECT } } return SMFIS_CONTINUE } # Register the callbacks and start up the filter milter register header bl_header milter register envfrom bl_envfrom milter main file delete /var/run/blmilter.sock
TclMilter is provided under the GNU General Public License (GPL) (see the license.terms
file for details).
Portions of this document, as well as the package its self, are based on the Milter API specification.
Milter API, RFC 1893, RFC 2034, RFC 2821, RFC 821
email, filter, milter, sendmail, spam, tcl
Copyright © 2006 Muonics, Inc.