Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
P
pymilter
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Deploy
Releases
Container registry
Model registry
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
misc
pymilter
Commits
f6a3b57f
Commit
f6a3b57f
authored
16 years ago
by
Stuart Gathman
Browse files
Options
Downloads
Patches
Plain Diff
enable_protocols class decorator, doc updates
parent
3428477e
No related branches found
No related tags found
No related merge requests found
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
Milter/__init__.py
+55
-17
55 additions, 17 deletions
Milter/__init__.py
doc/milter.py
+15
-0
15 additions, 0 deletions
doc/milter.py
with
70 additions
and
17 deletions
Milter/__init__.py
+
55
−
17
View file @
f6a3b57f
...
@@ -40,6 +40,41 @@ OPTIONAL_CALLBACKS = {
...
@@ -40,6 +40,41 @@ OPTIONAL_CALLBACKS = {
'
header
'
:(
P_NR_HDR
,
P_NOHDRS
)
'
header
'
:(
P_NR_HDR
,
P_NOHDRS
)
}
}
def
decode_mask
(
bits
,
names
):
t
=
[
(
s
,
getattr
(
milter
,
s
))
for
s
in
names
]
nms
=
[
s
for
s
,
m
in
t
if
bits
&
m
]
for
s
,
m
in
t
:
bits
&=
~
m
if
bits
:
nms
+=
hex
(
bits
)
return
nms
## Class decorator to enable optional protocol steps.
# P_SKIP is enabled by default when supported, but
# milter applications may wish to enable P_HDR_LEADSPC
# to send and receive the leading space of header continuation
# lines unchanged, and/or P_RCPT_REJ to have recipients
# detected as invalid by the MTA passed to the envcrpt callback.
#
# Applications may want to check whether the protocol is actually
# supported by the MTA in use. The <code>_protocol</code>
# member is a bitmask of protocol options negotiated. So,
# for instance, if <code>self._protocol & Milter.P_RCPT_REJ</code>
# is true, then that feature was successfully negotiated with the MTA.
#
# Sample use:
# <pre>
# class myMilter(Milter.Base):
# def envrcpt(self,to,*params):
# return Milter.CONTINUE
# myMilter = Milter.enable_protocols(myMilter,Milter.P_RCPT_REJ)
# </pre>
# @since 0.9.3
# @param klass the milter application class to modify
# @param mask a bitmask of protocol steps to enable
# @return the modified milter class
def
enable_protocols
(
klass
,
mask
):
klass
.
_protocol_mask
=
klass
.
protocol_mask
()
&
~
mask
return
klass
## Function decorator to disable callback methods.
## Function decorator to disable callback methods.
# If the MTA supports it, tells the MTA not to call this callback,
# If the MTA supports it, tells the MTA not to call this callback,
# increasing efficiency. All the callbacks (except negotiate)
# increasing efficiency. All the callbacks (except negotiate)
...
@@ -48,6 +83,7 @@ OPTIONAL_CALLBACKS = {
...
@@ -48,6 +83,7 @@ OPTIONAL_CALLBACKS = {
# another milter and wants to disable a callback again.
# another milter and wants to disable a callback again.
# The disabled method should still return Milter.CONTINUE, in case the MTA does
# The disabled method should still return Milter.CONTINUE, in case the MTA does
# not support protocol negotiation.
# not support protocol negotiation.
# @since 0.9.2
def
nocallback
(
func
):
def
nocallback
(
func
):
try
:
try
:
func
.
milter_protocol
=
OPTIONAL_CALLBACKS
[
func
.
__name__
][
1
]
func
.
milter_protocol
=
OPTIONAL_CALLBACKS
[
func
.
__name__
][
1
]
...
@@ -62,6 +98,7 @@ def nocallback(func):
...
@@ -62,6 +98,7 @@ def nocallback(func):
# CONTINUE in case the MTA does not support protocol negotiation.
# CONTINUE in case the MTA does not support protocol negotiation.
# The decorator arranges to change the return code to NOREPLY
# The decorator arranges to change the return code to NOREPLY
# when supported by the MTA.
# when supported by the MTA.
# @since 0.9.2
def
noreply
(
func
):
def
noreply
(
func
):
try
:
try
:
nr_mask
=
OPTIONAL_CALLBACKS
[
func
.
__name__
][
0
]
nr_mask
=
OPTIONAL_CALLBACKS
[
func
.
__name__
][
0
]
...
@@ -81,6 +118,7 @@ def noreply(func):
...
@@ -81,6 +118,7 @@ def noreply(func):
# connection in the negotiate callback. If the application then calls
# connection in the negotiate callback. If the application then calls
# the feature anyway via an instance method, this exception is
# the feature anyway via an instance method, this exception is
# thrown.
# thrown.
# @since 0.9.2
class
DisabledAction
(
RuntimeError
):
class
DisabledAction
(
RuntimeError
):
pass
pass
...
@@ -89,7 +127,7 @@ class DisabledAction(RuntimeError):
...
@@ -89,7 +127,7 @@ class DisabledAction(RuntimeError):
# unless they are using the low lever milter module directly.
# unless they are using the low lever milter module directly.
# All optional callbacks are disabled, and automatically
# All optional callbacks are disabled, and automatically
# reenabled when overridden.
# reenabled when overridden.
#
#
@since 0.9.2
class
Base
(
object
):
class
Base
(
object
):
"
The core class interface to the milter module.
"
"
The core class interface to the milter module.
"
...
@@ -109,6 +147,7 @@ class Base(object):
...
@@ -109,6 +147,7 @@ class Base(object):
# CHGHDRS,QUARANTINE,CHGFROM,SETSMLIST</code>.
# CHGHDRS,QUARANTINE,CHGFROM,SETSMLIST</code>.
# The <code>Milter.CURR_ACTS</code> bitmask is all actions
# The <code>Milter.CURR_ACTS</code> bitmask is all actions
# known when the milter module was compiled.
# known when the milter module was compiled.
# @since 0.9.2
#
#
## @var _protocol
## @var _protocol
...
@@ -121,6 +160,7 @@ class Base(object):
...
@@ -121,6 +160,7 @@ class Base(object):
# P_NR_EOH P_NR_BODY P_NR_HDR P_NOCONNECT P_NOHELO P_NOMAIL P_NORCPT
# P_NR_EOH P_NR_BODY P_NR_HDR P_NOCONNECT P_NOHELO P_NOMAIL P_NORCPT
# P_NODATA P_NOUNKNOWN P_NOEOH P_NOBODY P_NOHDRS P_HDR_LEADSPC P_SKIP
# P_NODATA P_NOUNKNOWN P_NOEOH P_NOBODY P_NOHDRS P_HDR_LEADSPC P_SKIP
# </code> (all under the Milter namespace).
# </code> (all under the Milter namespace).
# @since 0.9.2
## Defined by subclasses to write log messages.
## Defined by subclasses to write log messages.
def
log
(
self
,
*
msg
):
pass
def
log
(
self
,
*
msg
):
pass
...
@@ -159,6 +199,7 @@ class Base(object):
...
@@ -159,6 +199,7 @@ class Base(object):
## Called when the SMTP client says DATA.
## Called when the SMTP client says DATA.
# Returning REJECT rejects the message without wasting bandwidth
# Returning REJECT rejects the message without wasting bandwidth
# on the unwanted message.
# on the unwanted message.
# @since 0.9.2
@nocallback
@nocallback
def
data
(
self
):
return
CONTINUE
def
data
(
self
):
return
CONTINUE
## Called for each header field in the message body.
## Called for each header field in the message body.
...
@@ -173,6 +214,7 @@ class Base(object):
...
@@ -173,6 +214,7 @@ class Base(object):
def
body
(
self
,
blk
):
return
CONTINUE
def
body
(
self
,
blk
):
return
CONTINUE
## Called when the SMTP client issues an unknown command.
## Called when the SMTP client issues an unknown command.
# @param cmd the unknown command
# @param cmd the unknown command
# @since 0.9.2
@nocallback
@nocallback
def
unknown
(
self
,
cmd
):
return
CONTINUE
def
unknown
(
self
,
cmd
):
return
CONTINUE
## Called at the end of the message body.
## Called at the end of the message body.
...
@@ -194,12 +236,13 @@ class Base(object):
...
@@ -194,12 +236,13 @@ class Base(object):
# Libmilter passes the protocol bits that the current MTA knows
# Libmilter passes the protocol bits that the current MTA knows
# how to skip. We clear the ones we don't want to skip.
# how to skip. We clear the ones we don't want to skip.
# The negation is somewhat mind bending, but it is simple.
# The negation is somewhat mind bending, but it is simple.
# @since 0.9.2
@classmethod
@classmethod
def
protocol_mask
(
klass
):
def
protocol_mask
(
klass
):
try
:
try
:
return
klass
.
_protocol_mask
return
klass
.
_protocol_mask
except
AttributeError
:
except
AttributeError
:
p
=
0
p
=
P_RCPT_REJ
|
P_HDR_LEADSPC
# turn these new features off by default
for
func
,(
nr
,
nc
)
in
OPTIONAL_CALLBACKS
.
items
():
for
func
,(
nr
,
nc
)
in
OPTIONAL_CALLBACKS
.
items
():
func
=
getattr
(
klass
,
func
)
func
=
getattr
(
klass
,
func
)
ca
=
getattr
(
func
,
'
milter_protocol
'
,
0
)
ca
=
getattr
(
func
,
'
milter_protocol
'
,
0
)
...
@@ -212,11 +255,11 @@ class Base(object):
...
@@ -212,11 +255,11 @@ class Base(object):
# Default negotiation sets P_NO* and P_NR* for callbacks
# Default negotiation sets P_NO* and P_NR* for callbacks
# marked @@nocallback and @@noreply respectively, leaves all
# marked @@nocallback and @@noreply respectively, leaves all
# actions enabled, and enables Milter.SKIP.
# actions enabled, and enables Milter.SKIP.
# @since 0.9.2
def
negotiate
(
self
,
opts
):
def
negotiate
(
self
,
opts
):
try
:
try
:
self
.
_actions
,
p
,
f1
,
f2
=
opts
self
.
_actions
,
p
,
f1
,
f2
=
opts
opts
[
1
]
=
self
.
_protocol
=
\
opts
[
1
]
=
self
.
_protocol
=
p
&
~
self
.
protocol_mask
()
p
&
~
self
.
protocol_mask
()
&
~
P_RCPT_REJ
&
~
P_HDR_LEADSPC
opts
[
2
]
=
0
opts
[
2
]
=
0
opts
[
3
]
=
0
opts
[
3
]
=
0
#self.log("Negotiated:",opts)
#self.log("Negotiated:",opts)
...
@@ -241,9 +284,12 @@ class Base(object):
...
@@ -241,9 +284,12 @@ class Base(object):
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.
# The <code>Milter.
ADDHDRS
</code> action flag must be set.
# The <code>Milter.
SETSMLIST
</code> action flag must be set.
#
#
# May only be called from negotiate callback.
# May only be called from negotiate callback.
# @since 0.9.2
# @param stage the protocol stage to set to macro list for
# @param macros a string with a space delimited list of macros
def
setsmlist
(
self
,
stage
,
macros
):
def
setsmlist
(
self
,
stage
,
macros
):
if
not
self
.
_actions
&
SETSMLIST
:
raise
DisabledAction
(
"
SETSMLIST
"
)
if
not
self
.
_actions
&
SETSMLIST
:
raise
DisabledAction
(
"
SETSMLIST
"
)
if
type
(
macros
)
in
(
list
,
tuple
):
if
type
(
macros
)
in
(
list
,
tuple
):
...
@@ -319,6 +365,7 @@ class Base(object):
...
@@ -319,6 +365,7 @@ class Base(object):
# The <code>Milter.CHGFROM</code> action flag must be set.
# The <code>Milter.CHGFROM</code> action flag must be set.
#
#
# May be called from eom callback only.
# May be called from eom callback only.
# @since 0.9.1
# @param sender the new sender address
# @param sender the new sender address
# @param params an optional list of ESMTP parameters
# @param params an optional list of ESMTP parameters
def
chgfrom
(
self
,
sender
,
params
=
None
):
def
chgfrom
(
self
,
sender
,
params
=
None
):
...
@@ -452,7 +499,9 @@ def dictfromlist(args):
...
@@ -452,7 +499,9 @@ def dictfromlist(args):
## Convert ESMTP parm list to keyword dictionary.
## Convert ESMTP parm list to keyword dictionary.
# Params with no value are set to None in the dictionary.
# Params with no value are set to None in the dictionary.
# @since 0.9.3
# @param str list of param strings of the form "NAME" or "NAME=VALUE"
# @param str list of param strings of the form "NAME" or "NAME=VALUE"
# @return a dictionary of ESMTP param names and values
def
param2dict
(
str
):
def
param2dict
(
str
):
"
Convert ESMTP parm list to keyword dictionary.
"
"
Convert ESMTP parm list to keyword dictionary.
"
pairs
=
[
x
.
split
(
'
=
'
,
1
)
for
x
in
str
]
pairs
=
[
x
.
split
(
'
=
'
,
1
)
for
x
in
str
]
...
@@ -475,19 +524,8 @@ def envcallback(c,args):
...
@@ -475,19 +524,8 @@ def envcallback(c,args):
return
c
(
*
pargs
,
**
kw
)
return
c
(
*
pargs
,
**
kw
)
## Run the milter.
## Run the milter.
# The MTA can communicate with the milter by means of a
# unix, inet, or inet6 socket. By default, a unix domain socket
# is used. It must not exist,
# and sendmail will throw warnings if, eg, the file is under a
# group or world writable directory.
# <pre>
# setconn('unix:/var/run/pythonfilter')
# setconn('inet:8800') # listen on ANY interface
# setconn('inet:7871@@publichost') # listen on a specific interface
# setconn('inet6:8020')
# </pre>
# @param name the name of the milter known by the MTA
# @param name the name of the milter known by the MTA
# @param socketname the
descriptor of the unix socket
# @param socketname the
socket to be passed to <code>milter.setconn</code>
# @param timeout the time in secs the MTA should wait for a response before
# @param timeout the time in secs the MTA should wait for a response before
# considering this milter dead
# considering this milter dead
def
runmilter
(
name
,
socketname
,
timeout
=
0
):
def
runmilter
(
name
,
socketname
,
timeout
=
0
):
...
...
This diff is collapsed.
Click to expand it.
doc/milter.py
+
15
−
0
View file @
f6a3b57f
...
@@ -40,5 +40,20 @@ def main(): pass
...
@@ -40,5 +40,20 @@ def main(): pass
def
setdbg
(
lev
):
pass
def
setdbg
(
lev
):
pass
def
settimeout
(
secs
):
pass
def
settimeout
(
secs
):
pass
def
setbacklog
(
n
):
pass
def
setbacklog
(
n
):
pass
## Set the socket used to communicate with the MTA.
# The MTA can communicate with the milter by means of a
# unix, inet, or inet6 socket. By default, a unix domain socket
# is used. It must not exist,
# and sendmail will throw warnings if, eg, the file is under a
# group or world writable directory.
# <pre>
# setconn('unix:/var/run/pythonfilter')
# setconn('inet:8800') # listen on ANY interface
# setconn('inet:7871@@publichost') # listen on a specific interface
# setconn('inet6:8020')
# </pre>
def
setconn
(
s
):
pass
def
setconn
(
s
):
pass
## Stop the milter gracefully.
def
stop
():
pass
def
stop
():
pass
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment