Compare commits

..

7 Commits

Author SHA1 Message Date
cvs2svn e5bf260f30 This commit was manufactured by cvs2svn to create tag 'pymilter-0_9_6'.
Sprout from master 2012-03-03 18:51:56 UTC Stuart Gathman <stuart@gathman.org> 'Release 0.9.6'
Cherrypick from bmsi 2005-05-31 18:23:49 UTC Stuart Gathman <stuart@gathman.org> 'Development changes since 0.7.2':
    sample.py
    test/big5
    test/bounce
    test/bounce1
    test/bound
    test/honey
    test/missingboundary
    test/samp1
    test/spam44
    test/spam7
    test/spam8
    test/test1
    test/test8
    test/virus1
    test/virus13
    test/virus2
    test/virus3
    test/virus4
    test/virus5
    test/virus6
    test/virus7
2012-03-03 18:51:57 +00:00
Stuart Gathman 8f4a82794c Release 0.9.6 2012-03-03 18:51:56 +00:00
Stuart Gathman de0ec3430d Start new release. 2012-02-26 01:35:58 +00:00
Stuart Gathman c9e32e4b06 throw ValueError when message line contains a single % 2012-02-25 15:53:45 +00:00
Stuart Gathman 83a1762515 New example 2011-11-05 15:51:03 +00:00
Stuart Gathman feb6526cb8 Grace period 2011-11-05 15:50:02 +00:00
Stuart Gathman 3a3add814e Very simple actual milter. 2011-11-05 14:28:56 +00:00
8 changed files with 111 additions and 13 deletions
+17 -3
View File
@@ -310,7 +310,7 @@ class Base(object):
## Called when the connection is closed. ## Called when the connection is closed.
def close(self): return CONTINUE def close(self): return CONTINUE
## Return mask of SMFIP_N.. protocol option bits to clear for this class ## Return mask of SMFIP_N* protocol option bits to clear for this class
# The @@nocallback and @@noreply decorators set the # The @@nocallback and @@noreply decorators set the
# <code>milter_protocol</code> function attribute to the protocol mask bit to # <code>milter_protocol</code> function attribute to the protocol mask bit to
# pass to libmilter, causing that callback or its reply to be skipped. # pass to libmilter, causing that callback or its reply to be skipped.
@@ -336,7 +336,10 @@ class Base(object):
## Negotiate milter protocol options. Called by the ## Negotiate milter protocol options. Called by the
# <a href="https://www.milter.org/developers/api/xxfi_negotiate"> # <a href="https://www.milter.org/developers/api/xxfi_negotiate">
# xffi_negotiate</a> callback. # xffi_negotiate</a> callback. This is an advanced callback,
# do not override unless you know what you are doing. Most
# negotiation can be done simply by using the supplied
# class and function decorators.
# Options are passed as # Options are passed as
# a list of 4 32-bit ints which can be modified and are passed # a list of 4 32-bit ints which can be modified and are passed
# back to libmilter on return. # back to libmilter on return.
@@ -363,14 +366,24 @@ class Base(object):
## Return the value of an MTA macro. Sendmail macro names ## Return the value of an MTA macro. Sendmail macro names
# are either single chars (e.g. "j") or multiple chars enclosed # are either single chars (e.g. "j") or multiple chars enclosed
# in braces (e.g. "{auth_type}"). Macro names are MTA dependent. # in braces (e.g. "{auth_type}"). Macro names are MTA dependent.
# See <a href="https://www.milter.org/developers/api/smfi_getsymval">
# smfi_getsymval</a> for default sendmail macros.
# @param sym the macro name # @param sym the macro name
def getsymval(self,sym): def getsymval(self,sym):
return self._ctx.getsymval(sym) return self._ctx.getsymval(sym)
## Set the SMTP reply code and message. ## Set the SMTP reply code and message.
# If the MTA does not support setmlreply, then only the # If the MTA does not support setmlreply, then only the
# first msg line is used. # first msg line is used. Any '%' in a message line
# must be doubled, or libmilter will silently ignore the setreply.
# Beginning with 0.9.6, we test for that case and throw ValueError to avoid
# head scratching. What will <i>really</i> irritate you, however,
# is that if you carefully double any '%', your message will be
# sent - but with the '%' still doubled!
def setreply(self,rcode,xcode=None,msg=None,*ml): def setreply(self,rcode,xcode=None,msg=None,*ml):
for m in (msg,)+ml:
if 1 in [len(s)&1 for s in R.findall(m)]:
raise ValueError("'%' must be doubled: "+m)
return self._ctx.setreply(rcode,xcode,msg,*ml) return self._ctx.setreply(rcode,xcode,msg,*ml)
## Tell the MTA which macro names will be used. ## Tell the MTA which macro names will be used.
@@ -714,4 +727,5 @@ for priv in ('os','milter','thread','factory','_seq','_seq_lock','__version__'):
__all__ = __all__.keys() __all__ = __all__.keys()
## @example milter-template.py ## @example milter-template.py
## @example milter-nomix.py
# #
+1 -1
View File
@@ -50,7 +50,7 @@ class Greylist(object):
# expired # expired
log.debug('Expired greylist: %s',key) log.debug('Expired greylist: %s',key)
r = Record() r = Record()
elif now < r.firstseen + self.greylist_time: elif now < r.firstseen + self.greylist_time + 5:
# still greylisted # still greylisted
log.debug('Early greylist: %s',key) log.debug('Early greylist: %s',key)
#r = Record() #r = Record()
+2 -2
View File
@@ -2,8 +2,8 @@ web:
doxygen doxygen
rsync -ravK doc/html/ spidey2.bmsi.com:/Public/pymilter rsync -ravK doc/html/ spidey2.bmsi.com:/Public/pymilter
VERSION=0.9.5 VERSION=0.9.6
CVSTAG=pymilter-0_9_5 CVSTAG=pymilter-0_9_6
PKG=pymilter-$(VERSION) PKG=pymilter-$(VERSION)
SRCTAR=$(PKG).tar.gz SRCTAR=$(PKG).tar.gz
+79
View File
@@ -0,0 +1,79 @@
## A very simple milter to prevent mixing of internal and external mail.
# Internal is defined as using one of a list of internal top level domains.
# This code is open-source on the same terms as Python.
import Milter
import time
import sys
from Milter.utils import parse_addr
internal_tlds = ["corp", "personal"]
## Determine if a hostname is internal or not.
# True if internal, False otherwise
def is_internal(hostname):
components = hostname.split(".")
return components.pop() in internal_tlds:
# Determine if internal and external hosts are mixed based on a list
# of hostnames
def are_mixed(hostnames):
hostnames_mapped = map(is_internal, hostnames)
# Num internals
num_internal_hosts = hostnames_mapped.count(True)
# Num externals
num_external_hosts = hostnames_mapped.count(False)
return num_external_hosts >= 1 and num_internal_hosts >= 1
class NoMixMilter(Milter.Base):
def __init__(self): # A new instance with each new connection.
self.id = Milter.uniqueID() # Integer incremented with each call.
## def envfrom(self,f,*str):
@Milter.noreply
def envfrom(self, mailfrom, *str):
self.mailfrom = mailfrom
self.domains = []
t = parse_addr(mailfrom)
if len(t) > 1:
self.domains.append(t[1])
else:
self.domains.append('local')
self.internal = False
return Milter.CONTINUE
## def envrcpt(self, to, *str):
def envrcpt(self, to, *str):
self.R.append(to)
t = parse_addr(to)
if len(t) > 1:
self.domains.append(t[1])
else:
self.domains.append('local')
if are_mixed(self.domains):
# FIXME: log recipients collected in self.mailfrom and self.R
self.setreply('550','5.7.1','Mixing internal and external TLDs')
return Milter.REJECT
return Milter.CONTINUE
def main():
socketname = "/var/run/nomixsock"
timeout = 600
# Register to have the Milter factory create instances of your class:
Milter.factory = NoMixMilter
print "%s milter startup" % time.strftime('%Y%b%d %H:%M:%S')
sys.stdout.flush()
Milter.runmilter("nomixfilter",socketname,timeout)
logq.put(None)
bt.join()
print "%s nomix milter shutdown" % time.strftime('%Y%b%d %H:%M:%S')
if __name__ == "__main__":
main()
+1 -2
View File
@@ -79,7 +79,7 @@ class myMilter(Milter.Base):
## def envrcpt(self, to, *str): ## def envrcpt(self, to, *str):
@Milter.noreply @Milter.noreply
def envrcpt(self, recipient, *str): def envrcpt(self, to, *str):
rcptinfo = to,Milter.dictfromlist(str) rcptinfo = to,Milter.dictfromlist(str)
self.R.append(rcptinfo) self.R.append(rcptinfo)
@@ -110,7 +110,6 @@ class myMilter(Milter.Base):
self.addrcpt('<%s>' % 'spy@example.com') self.addrcpt('<%s>' % 'spy@example.com')
return Milter.ACCEPT return Milter.ACCEPT
def close(self): def close(self):
# always called, even when abort is called. Clean up # always called, even when abort is called. Clean up
# any external resources here. # any external resources here.
+4 -2
View File
@@ -1,4 +1,7 @@
# $Log$ # $Log$
# Revision 1.7 2009/06/13 21:15:12 customdesigned
# Doxygen updates.
#
# Revision 1.6 2009/06/09 03:13:13 customdesigned # Revision 1.6 2009/06/09 03:13:13 customdesigned
# More doxygen docs. # More doxygen docs.
# #
@@ -165,15 +168,14 @@ class MimeMessage(Message):
""" """
def __init__(self,fp=None,seekable=1): def __init__(self,fp=None,seekable=1):
Message.__init__(self) Message.__init__(self)
self.headerchange = None
self.submsg = None self.submsg = None
self.modified = False self.modified = False
## @var headerchange ## @var headerchange
# Provide a headerchange event for integration with Milter. # Provide a headerchange event for integration with Milter.
# The headerchange attribute can be assigned a function to be called when # The headerchange attribute can be assigned a function to be called when
# changing headers. The signature is: # changing headers. The signature is:
# headerchange(msg,name,value) -> None # headerchange(msg,name,value) -> None
self.headerchange = None
def get_param(self, param, failobj=None, header='content-type', unquote=True): def get_param(self, param, failobj=None, header='content-type', unquote=True):
val = Message.get_param(self,param,failobj,header,unquote) val = Message.get_param(self,param,failobj,header,unquote)
+6 -2
View File
@@ -6,7 +6,7 @@
Summary: Python interface to sendmail milter API Summary: Python interface to sendmail milter API
Name: %{pythonbase}-pymilter Name: %{pythonbase}-pymilter
Version: 0.9.5 Version: 0.9.6
Release: 1%{dist} Release: 1%{dist}
Source: http://downloads.sourceforge.net/pymilter/pymilter-%{version}.tar.gz Source: http://downloads.sourceforge.net/pymilter/pymilter-%{version}.tar.gz
License: GPLv2+ License: GPLv2+
@@ -75,7 +75,11 @@ chmod a+x $RPM_BUILD_ROOT%{libdir}/start.sh
rm -rf $RPM_BUILD_ROOT rm -rf $RPM_BUILD_ROOT
%changelog %changelog
* Wed Mar 02 2010 Stuart Gathman <stuart@bmsi.com> 0.9.5-1 * Sat Feb 25 2012 Stuart Gathman <stuart@bmsi.com> 0.9.6-1
- Raise ValueError on unescaped '%' passed to setreply
- Grace time at end of Greylist window
* Fri Aug 19 2011 Stuart Gathman <stuart@bmsi.com> 0.9.5-1
- Print milter.error for invalid callback return type. - Print milter.error for invalid callback return type.
(Since stacktrace is empty, the TypeError exception is confusing.) (Since stacktrace is empty, the TypeError exception is confusing.)
- Fix milter-template.py - Fix milter-template.py
+1 -1
View File
@@ -13,7 +13,7 @@ libs = ["milter"]
libdirs = ["/usr/lib/libmilter"] # needed for Debian libdirs = ["/usr/lib/libmilter"] # needed for Debian
# NOTE: importing Milter to obtain version fails when milter.so not built # NOTE: importing Milter to obtain version fails when milter.so not built
setup(name = "pymilter", version = '0.9.5', setup(name = "pymilter", version = '0.9.6',
description="Python interface to sendmail milter API", description="Python interface to sendmail milter API",
long_description="""\ long_description="""\
This is a python extension module to enable python scripts to This is a python extension module to enable python scripts to