### Sendmail configuration example Source: https://pythonhosted.org/pymilter/milter_api/installation.html Example of how to define multiple input mail filters in a sendmail.mc file, specifying socket types, flags, and timeouts. ```m4 INPUT_MAIL_FILTER(`filter1', `S=unix:/var/run/f1.sock, F=R') INPUT_MAIL_FILTER(`filter2', `S=unix:/var/run/f2.sock, F=T, T=S:1s;R:1s;E:5m') INPUT_MAIL_FILTER(`filter3', `S=inet:999@localhost, T=C:2m') define(`confINPUT_MAIL_FILTERS', `filter2,filter1,filter3') ``` -------------------------------- ### Milter Example Source: https://pythonhosted.org/pymilter/milter-template_8py-example.html This code snippet demonstrates a basic Python Milter implementation, including thread management, socket setup, Milter factory registration, flag setting, and running the Milter. ```python def main(): bt = Thread(target=background) bt.start() socketname = "/home/stuart/pythonsock" timeout = 600 # Register to have the Milter factory create instances of your class: Milter.factory = myMilter flags = Milter.CHGBODY + Milter.CHGHDRS + Milter.ADDHDRS flags += Milter.ADDRCPT flags += Milter.DELRCPT Milter.set_flags(flags) # tell Sendmail which features we use print "%s milter startup" % time.strftime('%Y%b%d %H:%M:%S') sys.stdout.flush() Milter.runmilter("pythonfilter",socketname,timeout) logq.put(None) bt.join() print "%s bms milter shutdown" % time.strftime('%Y%b%d %H:%M:%S') if __name__ == "__main__": main() ``` -------------------------------- ### EXAMPLE Source: https://pythonhosted.org/pymilter/milter_api/smfi_insheader.html An example of how to use smfi_insheader. ```c int ret; SMFICTX *ctx; ... ret = smfi_insheader(ctx, 0, "First", "See me?"); ``` -------------------------------- ### EXAMPLE Source: https://pythonhosted.org/pymilter/milter_api/smfi_addheader.html An example of how to use smfi_addheader to add a Content-Type header. ```c int ret; SMFICTX *ctx; ... ret = smfi_addheader(ctx, "Content-Type", "multipart/mixed;\n\tboundary=\"foobar\""); ``` -------------------------------- ### inet_pton Examples Source: https://pythonhosted.org/pymilter/namespaceMilter_1_1pyip6.html Examples demonstrating the conversion of ip6 standard hex notation to ip6 address structures using the inet_pton function. ```python >>> struct.unpack('!HHHHHHHH',inet_pton('::')) (0, 0, 0, 0, 0, 0, 0, 0) >>> struct.unpack('!HHHHHHHH',inet_pton('::1234')) (0, 0, 0, 0, 0, 0, 0, 4660) >>> struct.unpack('!HHHHHHHH',inet_pton('1234::')) (4660, 0, 0, 0, 0, 0, 0, 0) >>> struct.unpack('!HHHHHHHH',inet_pton('1234::5678')) (4660, 0, 0, 0, 0, 0, 0, 22136) >>> struct.unpack('!HHHHHHHH',inet_pton('::FFFF:1.2.3.4')) (0, 0, 0, 0, 0, 65535, 258, 772) >>> struct.unpack('!HHHHHHHH',inet_pton('1.2.3.4')) (0, 0, 0, 0, 0, 65535, 258, 772) >>> try: inet_pton('::1.2.3.4.5') ... except ValueError,x: print x ::1.2.3.4.5 ``` -------------------------------- ### Compile object file Source: https://pythonhosted.org/pymilter/milter_api/installation.html Compile a C source file into an object file, including specified include paths. ```c cc -I/path/to/include -I/path/to/sendmail -c myfile.c ``` -------------------------------- ### Set Connection Examples Source: https://pythonhosted.org/pymilter/namespacemilter.html Examples of how to set the socket for communication with the MTA using milter.setconn(). ```python milter.setconn('unix:/var/run/pythonfilter') # a named pipe milter.setconn('local:/var/run/pythonfilter') # a named pipe milter.setconn('inet:8800') # listen on ANY interface milter.setconn('inet:7871@publichost') # listen on a specific interface milter.setconn('inet6:8020') milter.setconn('inet6:8020@[2001:db8:1234::1]') # listen on specific IP ``` -------------------------------- ### inet_ntop Examples Source: https://pythonhosted.org/pymilter/namespaceMilter_1_1pyip6.html Examples demonstrating the conversion of ip6 address structures to standard hex notation using the inet_ntop function. ```python >>> inet_ntop(struct.pack("!HHHHHHHH",0,0,0,0,0,0xFFFF,0x0102,0x0304)) '::FFFF:1.2.3.4' >>> inet_ntop(struct.pack("!HHHHHHHH",0x1234,0x5678,0,0,0,0,0x0102,0x0304)) '1234:5678::102:304' >>> inet_ntop(struct.pack("!HHHHHHHH",0,0,0,0x1234,0x5678,0,0x0102,0x0304)) '::1234:5678:0:102:304' >>> inet_ntop(struct.pack("!HHHHHHHH",0x1234,0x5678,0,0x0102,0x0304,0,0,0)) '1234:5678:0:102:304::' >>> inet_ntop(struct.pack("!HHHHHHHH",0,0,0,0,0,0,0,0)) '::' ``` -------------------------------- ### EXAMPLE Source: https://pythonhosted.org/pymilter/milter_api/smfi_chgheader.html Example of how to use smfi_chgheader to change a header's value. ```c int ret; SMFICTX *ctx; ... ret = smfi_chgheader(ctx, "Content-Type", 1, "multipart/mixed;\n\tboundary=\"foobar\""); ``` -------------------------------- ### parse_addr example Source: https://pythonhosted.org/pymilter/namespaceMilter_1_1utils.html Split email into user,domain. ```python def Milter.utils.parse_addr | ( | | _t_ | ) | ---|---|---|---|---|--- ``` Split email into user,domain. >>> parse_addr('user@example.com') ['user', 'example.com'] >>> parse_addr('"user@example.com"') ['user@example.com'] >>> parse_addr('"user@bar"@example.com') ['user@bar', 'example.com'] >>> parse_addr('foo') ['foo'] >>> parse_addr('@mx.example.com:user@example.com') ['user', 'example.com'] >>> parse_addr('@user@example.com') ['@user', 'example.com'] ``` ``` -------------------------------- ### Link object files into an executable filter Source: https://pythonhosted.org/pymilter/milter_api/installation.html Link object files to create an executable filter, specifying library location and linking with libmilter and pthreads. ```c cc -o myfilter [object-files] -L[library-location] -lmilter -pthread ``` -------------------------------- ### Generate sendmail configuration file Source: https://pythonhosted.org/pymilter/milter_api/installation.html Command to process the .mc configuration file using m4 to generate the .cf configuration file. ```bash m4 ../m4/cf.m4 myconfig.mc > myconfig.cf ``` -------------------------------- ### parseaddr example Source: https://pythonhosted.org/pymilter/namespaceMilter_1_1utils.html Split email into Fullname and address. ```python def Milter.utils.parseaddr | ( | | _t_ | ) | ---|---|---|---|---|--- Split email into Fullname and address. This replaces `email.Utils.parseaddr` but fixes some tricky test cases. Additional tricky cases are still broken. Patches welcome. ``` Split email into Fullname and address. >>> parseaddr('user@example.com') ('', 'user@example.com') >>> parseaddr('"Full Name" ') ('Full Name', 'foo@example.com') >>> parseaddr('spam@spammer.com ') ('spam@spammer.com', 'foo@example.com') >>> parseaddr('God@heaven <@hop1.org,@hop2.net:jeff@spec.org>') ('God@heaven', 'jeff@spec.org') >>> parseaddr('Real Name ((comment)) ') ('Real Name', 'addr...@example.com') >>> parseaddr('a(WRONG)@b') ('WRONG', 'a@b') ``` ``` -------------------------------- ### Example usage of smfi_setmlreply Source: https://pythonhosted.org/pymilter/milter_api/smfi_setmlreply.html An example demonstrating how to use smfi_setmlreply to set a custom multi-line SMTP error reply. ```c ret = smfi_setmlreply(ctx, "550", "5.7.0", "Spammer access rejected", "Please see our policy at:", "http://www.example.com/spampolicy.html", NULL); ``` -------------------------------- ### Generated .cf file content Source: https://pythonhosted.org/pymilter/milter_api/installation.html The resulting lines added to the sendmail .cf file based on the INPUT_MAIL_FILTER definitions. ```text Xfilter1, S=unix:/var/run/f1.sock, F=R Xfilter2, S=unix:/var/run/f2.sock, F=T, T=S:1s;R:1s;E:5m Xfilter3, S=inet:999@localhost, T=C:2m O InputMailFilters=filter2,filter1,filter3 ``` -------------------------------- ### Enable Optional Protocol Steps Source: https://pythonhosted.org/pymilter/namespaceMilter.html Example of using the enable_protocols decorator to enable the P_RCPT_REJ flag. ```python class myMilter(Milter.Base): def envrcpt(self,to,*params): return Milter.CONTINUE myMilter = Milter.enable_protocols(myMilter,Milter.P_RCPT_REJ) ``` -------------------------------- ### Example SMTP response Source: https://pythonhosted.org/pymilter/milter_api/smfi_setmlreply.html The resulting SMTP response after calling the smfi_setmlreply example. ```text 550-5.7.0 Spammer access rejected 550-5.7.0 Please see our policy at: 550 5.7.0 http://www.example.com/spampolicy.html ``` -------------------------------- ### envrcpt function Source: https://pythonhosted.org/pymilter/namespaceMilter.html Example implementation of the envrcpt function within a Milter class. ```python @Milter.rejected_recipients class myMilter(Milter.Base): def envrcpt(self,to,*params): return Milter.CONTINUE ``` -------------------------------- ### Background Logging Function Source: https://pythonhosted.org/pymilter/milter-template_8py-example.html A separate function that runs in a background thread to process and print log messages from the milter. ```python def background(): while True: t = logq.get() if not t: break msg,id,ts = t print "%s [%d]" % (time.strftime('%Y%b%d %H:%M:%S',time.localtime(ts)),id), for i in msg: print i, print ``` -------------------------------- ### Milter Macros Configuration Source: https://pythonhosted.org/pymilter/classMilter_1_1Base.html Configuration examples for enabling Milter macros in sendmail.cf or sendmail.mc to access connection details like daemon port. ```text O Milter.macros.connect=j, _, {daemon_name}, {daemon_port}, {if_name}, {if_addr} ``` ```text define(`confMILTER_MACROS_CONNECT', ``j, _, {daemon_name}, {daemon_port}, {if_name}, {if_addr}'')dnl ``` -------------------------------- ### parse_header example Source: https://pythonhosted.org/pymilter/namespaceMilter_1_1utils.html Decode headers gratuitously encoded to hide the content. ```python def Milter.utils.parse_header | ( | | _val_ | ) | ---|---|---|---|---|--- Decode headers gratuitously encoded to hide the content. Spammers often encode headers to obscure the content from spam filters. This function decodes gratuitously encoded headers. **Parameters:** | _val_ | the raw header value ---|---|--- **Returns:** the decoded value or the original raw value ``` Decode headers gratuitously encoded to hide the content. ``` ``` -------------------------------- ### Header Field Preservation Example (Milter Input) Source: https://pythonhosted.org/pymilter/milter_api/xxfi_header.html Shows the input received by a milter when SMFIP_HDR_LEADSPC is enabled, preserving leading spaces. ```text "From", " sender " "To", " user " "Subject", "no" ``` -------------------------------- ### Milter Class Definition Source: https://pythonhosted.org/pymilter/milter-template_8py-example.html Defines a custom milter class 'myMilter' that extends the base Milter class and implements various milter event handlers. ```python ## To roll your own milter, create a class that extends Milter. # See the pymilter project at http://bmsi.com/python/milter.html # based on Sendmail's milter API # This code is open-source on the same terms as Python. ## Milter calls methods of your class at milter events. ## Return REJECT,TEMPFAIL,ACCEPT to short circuit processing for a message. ## You can also add/del recipients, replacebody, add/del headers, etc. import Milter import StringIO import time import email import sys from socket import AF_INET, AF_INET6 from Milter.utils import parse_addr if True: from multiprocessing import Process as Thread, Queue else: from threading import Thread from Queue import Queue logq = Queue(maxsize=4) class myMilter(Milter.Base): def __init__(self): self.id = Milter.uniqueID() @Milter.noreply def connect(self, IPname, family, hostaddr): self.IP = hostaddr[0] self.port = hostaddr[1] if family == AF_INET6: self.flow = hostaddr[2] self.scope = hostaddr[3] else: self.flow = None self.scope = None self.IPname = IPname self.H = None self.fp = None self.receiver = self.getsymval('j') self.log("connect from %s at %s" % (IPname, hostaddr) ) return Milter.CONTINUE ## def hello(self,hostname): def hello(self, heloname): self.H = heloname self.log("HELO %s" % heloname) if heloname.find('.') < 0: self.setreply('550','5.7.1','Sheesh people! Use a proper helo name!') return Milter.REJECT return Milter.CONTINUE ## def envfrom(self,f,*str): def envfrom(self, mailfrom, *str): self.F = mailfrom self.R = [] self.fromparms = Milter.dictfromlist(str) self.user = self.getsymval('{auth_authen}') self.log("mail from:", mailfrom, *str) self.fp = StringIO.StringIO() self.canon_from = '@'.join(parse_addr(mailfrom)) self.fp.write('From %s %s\n' % (self.canon_from,time.ctime())) return Milter.CONTINUE ## def envrcpt(self, to, *str): @Milter.noreply def envrcpt(self, to, *str): rcptinfo = to,Milter.dictfromlist(str) self.R.append(rcptinfo) return Milter.CONTINUE @Milter.noreply def header(self, name, hval): self.fp.write("%s: %s\n" % (name,hval)) return Milter.CONTINUE @Milter.noreply def eoh(self): self.fp.write("\n") return Milter.CONTINUE @Milter.noreply def body(self, chunk): self.fp.write(chunk) return Milter.CONTINUE def eom(self): self.fp.seek(0) msg = email.message_from_file(self.fp) self.addrcpt('<%s>' % 'spy@example.com') return Milter.ACCEPT def close(self): return Milter.CONTINUE def abort(self): return Milter.CONTINUE ## === Support Functions === def log(self,*msg): logq.put((msg,self.id,time.time())) ``` -------------------------------- ### usage function Source: https://pythonhosted.org/pymilter/milter_api/sample.html Prints the usage instructions for the program. ```c static void usage(prog) char *prog; { fprintf(stderr, "Usage: %s -p socket-addr [-t timeout] [-r reject-addr] [-a add-addr]\n", prog); } ``` -------------------------------- ### main function Source: https://pythonhosted.org/pymilter/milter_api/sample.html The main entry point of the program, handling command-line arguments and registering the filter. ```c int main(argc, argv) int argc; char **argv; { bool setconn = FALSE; int c; const char *args = "p:t:r:a:h"; extra char *optarg; /* Process command line options */ while ((c = getopt(argc, argv, args)) != -1) { switch (c) { case 'p': if (optarg == NULL || *optarg == '\0') { (void) fprintf(stderr, "Illegal conn: %s\n", optarg); exit(EX_USAGE); } if (smfi_setconn(optarg) == MI_FAILURE) { (void) fprintf(stderr, "smfi_setconn failed\n"); exit(EX_SOFTWARE); } /* ** If we're using a local socket, make sure it ** doesn't already exist. Don't ever run this ** code as root!! */ if (strncasecmp(optarg, "unix:", 5) == 0) unlink(optarg + 5); else if (strncasecmp(optarg, "local:", 6) == 0) unlink(optarg + 6); setconn = TRUE; break; case 't': if (optarg == NULL || *optarg == '\0') { (void) fprintf(stderr, "Illegal timeout: %s\n", optarg); exit(EX_USAGE); } if (smfi_settimeout(atoi(optarg)) == MI_FAILURE) { (void) fprintf(stderr, "smfi_settimeout failed\n"); exit(EX_SOFTWARE); } break; case 'r': if (optarg == NULL) { (void) fprintf(stderr, "Illegal reject rcpt: %s\n", optarg); exit(EX_USAGE); } reject = optarg; break; case 'a': if (optarg == NULL) { (void) fprintf(stderr, "Illegal add rcpt: %s\n", optarg); exit(EX_USAGE); } add = optarg; smfilter.xxfi_flags |= SMFIF_ADDRCPT; break; case 'h': default: usage(argv[0]); exit(EX_USAGE); } } if (!setconn) { fprintf(stderr, "%s: Missing required -p argument\n", argv[0]); usage(argv[0]); exit(EX_USAGE); } if (smfi_register(smfilter) == MI_FAILURE) { fprintf(stderr, "smfi_register failed\n"); exit(EX_UNAVAILABLE); } return smfi_main(); } /* eof */ ``` -------------------------------- ### mlfi_negotiate function Source: https://pythonhosted.org/pymilter/milter_api/sample.html Handles negotiation of protocol options, returning all options. ```c sfsistat mlfi_negotiate(ctx, f0, f1, f2, f3, pf0, pf1, pf2, pf3) SMFICTX *ctx; unsigned long f0; unsigned long f1; unsigned long f2; unsigned long f3; unsigned long *pf0; unsigned long *pf1; unsigned long *pf2; unsigned long *pf3; { return SMFIS_ALL_OPTS; } ``` -------------------------------- ### iniplist example Source: https://pythonhosted.org/pymilter/namespaceMilter_1_1utils.html Return whether ip is in cidr list ```python def Milter.utils.iniplist | ( | | _ipaddr_ , ---|---|---|--- | | | _iplist_| | ) | | | ``` Return whether ip is in cidr list >>> iniplist('66.179.26.146',['127.0.0.1','66.179.26.128/26']) True >>> iniplist('127.0.0.1',['127.0.0.1','66.179.26.128/26']) True >>> iniplist('192.168.0.45',['192.168.0.*']) True >>> iniplist('4.2.2.2',['b.resolvers.Level3.net']) True >>> iniplist('2607:f8b0:4004:801::',['google.com/64']) True >>> iniplist('4.2.2.2',['nothing.example.com']) False >>> iniplist('2001:610:779:0:223:6cff:fe9a:9cf3',['127.0.0.1','172.20.1.0/24','2001:610:779::/48']) True >>> iniplist('2G01:610:779:0:223:6cff:fe9a:9cf3',['127.0.0.1','172.20.1.0/24','2001:610:779::/48']) Traceback (most recent call last): ... ValueError: Invalid ip syntax:2G01:610:779:0:223:6cff:fe9a:9cf3 ``` ``` -------------------------------- ### mlfi_connect function Source: https://pythonhosted.org/pymilter/milter_api/sample.html Handles the connection event, allocates private data, and saves connection information. ```c sfsistat mlfi_connect(ctx, hostname, hostaddr) SMFICTX *ctx; char *hostname; _SOCK_ADDR *hostaddr; { struct mlfiPriv *priv; char *ident; /* allocate some private memory */ priv = malloc(sizeof *priv); if (priv == NULL) { /* can't accept this message right now */ return SMFIS_TEMPFAIL; } memset(priv, '\0', sizeof *priv); /* save the private data */ smfi_setpriv(ctx, priv); ident = smfi_getsymval(ctx, "_"); if (ident == NULL) ident = "???"; if ((priv->mlfi_connectfrom = strdup(ident)) == NULL) { (void) mlfi_cleanup(ctx, FALSE); return SMFIS_TEMPFAIL; } /* continue processing */ return SMFIS_CONTINUE; } ``` -------------------------------- ### Header Field Preservation Example Source: https://pythonhosted.org/pymilter/milter_api/xxfi_header.html Demonstrates how spaces after the colon in a header field are preserved with SMFIP_HDR_LEADSPC. ```text From: sender To: user Subject:no ``` -------------------------------- ### Sample Filter Header Source: https://pythonhosted.org/pymilter/milter_api/sample.html This is a sample header file for a milter filter, including necessary system and milter API includes. ```c #include #include #include #include #include #include #include #include #include "libmilter/mfapi.h" #ifndef bool # define bool int # define TRUE 1 ``` -------------------------------- ### mlfi_envfrom function Source: https://pythonhosted.org/pymilter/milter_api/sample.html Handles the envelope sender, opens a temporary file for message storage, and logs connection/sender information. ```c sfsistat mlfi_envfrom(ctx, argv) SMFICTX *ctx; char **argv; { int fd = -1; int argc = 0; struct mlfiPriv *priv = MLFIPRIV; char *mailaddr = smfi_getsymval(ctx, "{mail_addr}"); /* open a file to store this message */ if ((priv->mlfi_fname = strdup("/tmp/msg.XXXXXX")) == NULL) { (void) mlfi_cleanup(ctx, FALSE); return SMFIS_TEMPFAIL; } if ((fd = mkstemp(priv->mlfi_fname)) == -1) { (void) mlfi_cleanup(ctx, FALSE); return SMFIS_TEMPFAIL; } if ((priv->mlfi_fp = fdopen(fd, "w+")) == NULL) { (void) close(fd); (void) mlfi_cleanup(ctx, FALSE); return SMFIS_TEMPFAIL; } /* count the arguments */ while (*argv++ != NULL) ++argc; /* log the connection information we stored earlier: */ if (fprintf(priv->mlfi_fp, "Connect from %s (%s)\n\n", priv->mlfi_helofrom, priv->mlfi_connectfrom) == EOF) { (void) mlfi_cleanup(ctx, FALSE); return SMFIS_TEMPFAIL; } /* log the sender */ if (fprintf(priv->mlfi_fp, "FROM %s (%d argument%s)\n", mailaddr ? mailaddr : "???", argc, (argc == 1) ? "" : "s") == EOF) { (void) mlfi_cleanup(ctx, FALSE); return SMFIS_TEMPFAIL; } /* continue processing */ return SMFIS_CONTINUE; } ``` -------------------------------- ### mlfi_header function Source: https://pythonhosted.org/pymilter/milter_api/sample.html Handles header lines, writing them to the log file. ```c sfsistat mlfi_header(ctx, headerf, headerv) SMFICTX *ctx; char *headerf; unsigned char *headerv; { /* write the header to the log file */ if (fprintf(MLFIPRIV->mlfi_fp, "%s: %s\n", headerf, headerv) == EOF) { (void) mlfi_cleanup(ctx, FALSE); return SMFIS_TEMPFAIL; } /* continue processing */ return SMFIS_CONTINUE; } ``` -------------------------------- ### Handle Rejected Recipients Source: https://pythonhosted.org/pymilter/namespaceMilter.html Example of using the rejected_recipients decorator to enable a milter app to see all recipients if supported by the MTA. ```python @Milter.rejected_recipients class myMilter(Milter.Base): def envrcpt(self, to, *params): return Milter.CONTINUE ``` -------------------------------- ### Preserve Header Continuation Lines Source: https://pythonhosted.org/pymilter/namespaceMilter.html Example of using the header_leading_space decorator to preserve header continuation lines and leading space. ```python @Milter.header_leading_space class myMilter(Milter.Base): def header(self,hname,value): return Milter.CONTINUE ``` -------------------------------- ### mlfi_abort function Source: https://pythonhosted.org/pymilter/milter_api/sample.html Handles message abortion, initiating cleanup. ```c sfsistat mlfi_abort(ctx) SMFICTX *ctx; { return mlfi_cleanup(ctx, FALSE); } ``` -------------------------------- ### Header Field Preservation Example (Previous Behavior) Source: https://pythonhosted.org/pymilter/milter_api/xxfi_header.html Illustrates the input received by a milter without SMFIP_HDR_LEADSPC, where leading spaces are trimmed. ```text "From", "sender " "To", "user " "Subject", "no" ``` -------------------------------- ### sendmail.cf configuration for Milter.Milter.connect Source: https://pythonhosted.org/pymilter/classMilter_1_1Milter.html This snippet shows how to configure sendmail.cf to enable the necessary macros for the Milter.Milter.connect callback to access daemon port information. ```text O Milter.macros.connect=j, _, {daemon_name}, {daemon_port}, {if_name}, {if_addr} ``` -------------------------------- ### mlfi_eoh function Source: https://pythonhosted.org/pymilter/milter_api/sample.html Handles the end of headers, writing a blank line to the log file. ```c sfsistat mlfi_eoh(ctx) SMFICTX *ctx; { /* output the blank line between the header and the body */ if (fprintf(MLFIPRIV->mlfi_fp, "\n") == EOF) { (void) mlfi_cleanup(ctx, FALSE); return SMFIS_TEMPFAIL; } /* continue processing */ return SMFIS_CONTINUE; } ``` -------------------------------- ### sendmail.mc configuration for Milter.Milter.connect Source: https://pythonhosted.org/pymilter/classMilter_1_1Milter.html This snippet shows how to configure sendmail.mc to enable the necessary macros for the Milter.Milter.connect callback to access daemon port information. ```text define(`confMILTER_MACROS_CONNECT', ``j, _, {daemon_name}, {daemon_port}, {if_name}, {if_addr}'')dnl ``` -------------------------------- ### Session object cache Source: https://pythonhosted.org/pymilter/classMilter_1_1dns_1_1Session.html A Session object has a simple cache with no TTL that is valid for a single "session", for example an SMTP conversation. ```python A Session object has a simple cache with no TTL that is valid for a single "session", for example an SMTP conversation. ``` -------------------------------- ### Milter.Milter.envfrom callback signature and description Source: https://pythonhosted.org/pymilter/classMilter_1_1Milter.html This snippet shows the signature and a brief description of the envfrom callback, which is called to begin each message. ```python def Milter.Milter.envfrom ( _self_, f, str ) Called to begin each message. f -> string message sender str -> tuple additional ESMTP parameters ``` -------------------------------- ### SYNOPSIS Source: https://pythonhosted.org/pymilter/milter_api/smfi_chgheader.html Include the necessary header file and the function signature for smfi_chgheader. ```c #include int smfi_chgheader( SMFICTX *ctx, char *headerf, mi_int32 hdridx, char *headerv ); ``` -------------------------------- ### mlfi_helo function Source: https://pythonhosted.org/pymilter/milter_api/sample.html Handles the HELO command, retrieves TLS version and HELO host, and stores them. ```c sfsistat mlfi_helo(ctx, helohost) SMFICTX *ctx; char *helohost; { size_t len; char *tls; char *buf; struct mlfiPriv *priv = MLFIPRIV; ls = smfi_getsymval(ctx, "{tls_version}"); if (tls == NULL) ls = "No TLS"; if (helohost == NULL) helohost = "???"; len = strlen(tls) + strlen(helohost) + 3; if ((buf = (char*) malloc(len)) == NULL) { (void) mlfi_cleanup(ctx, FALSE); return SMFIS_TEMPFAIL; } snprintf(buf, len, "%s, %s", helohost, tls); if (priv->mlfi_helofrom != NULL) free(priv->mlfi_helofrom); priv->mlfi_helofrom = buf; /* continue processing */ return SMFIS_CONTINUE; } ``` -------------------------------- ### mlfi_unknown function Source: https://pythonhosted.org/pymilter/milter_api/sample.html Handles unknown SMTP commands by continuing processing. ```c sfsistat mlfi_unknown(ctx, cmd) SMFICTX *ctx; char *cmd; { return SMFIS_CONTINUE; } ``` -------------------------------- ### mlfi_envrcpt function Source: https://pythonhosted.org/pymilter/milter_api/sample.html Handles envelope recipients, checks against a reject list, and logs recipient information. ```c sfsistat mlfi_envrcpt(ctx, argv) SMFICTX *ctx; char **argv; { struct mlfiPriv *priv = MLFIPRIV; char *rcptaddr = smfi_getsymval(ctx, "{rcpt_addr}"); int argc = 0; /* count the arguments */ while (*argv++ != NULL) ++argc; /* log this recipient */ if (reject != NULL && rcptaddr != NULL && (strcasecmp(rcptaddr, reject) == 0)) { if (fprintf(priv->mlfi_fp, "RCPT %s -- REJECTED\n", rcptaddr) == EOF) { (void) mlfi_cleanup(ctx, FALSE); return SMFIS_TEMPFAIL; } return SMFIS_REJECT; } if (fprintf(priv->mlfi_fp, "RCPT %s (%d argument%s)\n", rcptaddr ? rcptaddr : "???", argc, (argc == 1) ? "" : "s") == EOF) { (void) mlfi_cleanup(ctx, FALSE); return SMFIS_TEMPFAIL; } /* continue processing */ return SMFIS_CONTINUE; } ``` -------------------------------- ### mlfi_data function Source: https://pythonhosted.org/pymilter/milter_api/sample.html Handles the DATA command by continuing processing. ```c sfsistat mlfi_data(ctx) SMFICTX *ctx; { return SMFIS_CONTINUE; } ``` -------------------------------- ### mlfi_body function Source: https://pythonhosted.org/pymilter/milter_api/sample.html Handles message body chunks, writing them to the log file. ```c sfsistat mlfi_body(ctx, bodyp, bodylen) SMFICTX *ctx; unsigned char *bodyp; size_t bodylen; { struct mlfiPriv *priv = MLFIPRIV; /* output body block to log file */ if (fwrite(bodyp, bodylen, 1, priv->mlfi_fp) != 1) { /* write failed */ fprintf(stderr, "Couldn't write file %s: %s\n", priv->mlfi_fname, strerror(errno)); (void) mlfi_cleanup(ctx, FALSE); return SMFIS_TEMPFAIL; } /* continue processing */ return SMFIS_CONTINUE; } ``` -------------------------------- ### smfilter structure definition Source: https://pythonhosted.org/pymilter/milter_api/sample.html Defines the properties and callback functions for the sample filter. ```c struct smfiDesc smfilter = { "SampleFilter", /* filter name */ SMFI_VERSION, /* version code -- do not change */ SMFIF_ADDHDRS|SMFIF_ADDRCPT, /* flags */ mlfi_connect, /* connection info filter */ mlfi_helo, /* SMTP HELO command filter */ mlfi_envfrom, /* envelope sender filter */ mlfi_envrcpt, /* envelope recipient filter */ mlfi_header, /* header filter */ mlfi_eoh, /* end of header */ mlfi_body, /* body block filter */ mlfi_eom, /* end of message */ mlfi_abort, /* message aborted */ mlfi_close, /* connection cleanup */ mlfi_unknown, /* unknown SMTP commands */ mlfi_data, /* DATA command */ mlfi_negotiate /* Once, at the start of each SMTP connection */ }; ``` -------------------------------- ### SAFE2CACHE initial value Source: https://pythonhosted.org/pymilter/classMilter_1_1dns_1_1Session.html Initial value for the SAFE2CACHE static attribute. ```python frozenset(( ('MX','MX'), ('MX','A'), ('CNAME','CNAME'), ('CNAME','A'), ('A','A'), ('AAAA','AAAA'), ('PTR','PTR'), ('NS','NS'), ('NS','A'), ('TXT','TXT'), ('SPF','SPF') )) ```