1## @package Milter
2# A thin OO wrapper for the milter module.
3#
4# Clients generally subclass Milter.Base and define callback
5# methods.
6#
7# @author Stuart D. Gathman <stuart@bmsi.com>
8# Copyright 2001,2009 Business Management Systems, Inc.
9# This code is under the GNU General Public License.  See COPYING for details.
10
11from __future__ import print_function
12__version__ = '1.0.4'
13
14import os
15import re
16import milter
17try:
18  import thread
19except:
20  # libmilter uses posix threads
21  import _thread as thread
22
23from milter import *
24from functools import wraps
25
26_seq_lock = thread.allocate_lock()
27_seq = 0
28
29def uniqueID():
30  """Return a unique sequence number (incremented on each call).
31  """
32  global _seq
33  _seq_lock.acquire()
34  seqno = _seq = _seq + 1
35  _seq_lock.release()
36  return seqno
37
38## @private
39OPTIONAL_CALLBACKS = {
40  'connect':(P_NR_CONN,P_NOCONNECT),
41  'hello':(P_NR_HELO,P_NOHELO),
42  'envfrom':(P_NR_MAIL,P_NOMAIL),
43  'envrcpt':(P_NR_RCPT,P_NORCPT),
44  'data':(P_NR_DATA,P_NODATA),
45  'unknown':(P_NR_UNKN,P_NOUNKNOWN),
46  'eoh':(P_NR_EOH,P_NOEOH),
47  'body':(P_NR_BODY,P_NOBODY),
48  'header':(P_NR_HDR,P_NOHDRS)
49}
50
51MACRO_CALLBACKS = {
52  'connect': M_CONNECT,
53  'hello': M_HELO, 'envfrom': M_ENVFROM, 'envrcpt': M_ENVRCPT,
54  'data': M_DATA, 'eom': M_EOM, 'eoh': M_EOH
55}
56
57## @private
58R = re.compile(r'%+')
59
60## @private
61def decode_mask(bits,names):
62  t = [ (s,getattr(milter,s)) for s in names]
63  nms = [s for s,m in t if bits & m]
64  for s,m in t: bits &= ~m
65  if bits: nms += hex(bits)
66  return nms
67
68## Class decorator to enable optional protocol steps.
69# P_SKIP is enabled by default when supported, but
70# applications may wish to enable P_HDR_LEADSPC
71# to send and receive the leading space of header continuation
72# lines unchanged, and/or P_RCPT_REJ to have recipients
73# detected as invalid by the MTA passed to the envcrpt callback.
74#
75# Applications may want to check whether the protocol is actually
76# supported by the MTA in use.  Base._protocol
77# is a bitmask of protocol options negotiated.  So,
78# for instance, if <code>self._protocol & Milter.P_RCPT_REJ</code>
79# is true, then that feature was successfully negotiated with the MTA
80# and the application will see recipients the MTA has flagged as invalid.
81#
82# Sample use:
83# <pre>
84# class myMilter(Milter.Base):
85#   def envrcpt(self,to,*params):
86#     return Milter.CONTINUE
87# myMilter = Milter.enable_protocols(myMilter,Milter.P_RCPT_REJ)
88# </pre>
89# @since 0.9.3
90# @param klass the %milter application class to modify
91# @param mask a bitmask of protocol steps to enable
92# @return the modified %milter class
93def enable_protocols(klass,mask):
94  klass._protocol_mask = klass.protocol_mask() & ~mask
95  return klass
96
97## Milter rejected recipients. A class decorator that calls
98# enable_protocols() with the P_RCPT_REJ flag.  By default, the MTA
99# does not pass recipients that it knows are invalid on to the milter.
100# This decorator enables a %milter app to see all recipients if supported
101# by the MTA.  Use like this with python-2.6 and later:
102# <pre>
103# @@Milter.rejected_recipients
104# class myMilter(Milter.Base):
105#   def envrcpt(self,to,*params):
106#     return Milter.CONTINUE
107# </pre>
108# @since 0.9.5
109# @param klass the %milter application class to modify
110# @return the modified %milter class
111def rejected_recipients(klass):
112  return enable_protocols(klass,P_RCPT_REJ)
113
114## Milter leading space on headers. A class decorator that calls
115# enable_protocols() with the P_HDR_LEADSPC flag.  By default,
116# header continuation lines are collected and joined before getting
117# sent to a milter.  Headers modified or added by the milter are
118# folded by the MTA as necessary according to its own standards.
119# With this flag, header continuation lines are preserved
120# with their newlines and leading space.  In addition, header folding
121# done by the milter is preserved as well.
122# Use like this with python-2.6 and later:
123# <pre>
124# @@Milter.header_leading_space
125# class myMilter(Milter.Base):
126#   def header(self,hname,value):
127#     return Milter.CONTINUE
128# </pre>
129# @since 0.9.5
130# @param klass the %milter application class to modify
131# @return the modified %milter class
132def header_leading_space(klass):
133  return enable_protocols(klass,P_HDR_LEADSPC)
134
135## Function decorator to disable callback methods.
136# If the MTA supports it, tells the MTA not to invoke this callback,
137# increasing efficiency.  All the callbacks (except negotiate)
138# are disabled in Milter.Base, and overriding them reenables the
139# callback.  An application may need to use @@nocallback when it extends
140# another %milter and wants to disable a callback again.
141# The disabled method should still return Milter.CONTINUE, in case the MTA does
142# not support protocol negotiation, and for when called from a test harness.
143# @since 0.9.2
144def nocallback(func):
145  try:
146    func.milter_protocol = OPTIONAL_CALLBACKS[func.__name__][1]
147  except KeyError:
148    raise ValueError(
149      '@nocallback applied to non-optional method: '+func.__name__)
150  @wraps(func)
151  def wrapper(self,*args):
152    if func(self,*args) != CONTINUE:
153      raise RuntimeError('%s return code must be CONTINUE with @nocallback'
154        % func.__name__)
155    return CONTINUE
156  return wrapper
157
158## Function decorator to disable callback reply.
159# If the MTA supports it, tells the MTA not to wait for a reply from
160# this callback, and assume CONTINUE.  The method should still return
161# CONTINUE in case the MTA does not support protocol negotiation.
162# The decorator arranges to change the return code to NOREPLY
163# when supported by the MTA.
164# @since 0.9.2
165def noreply(func):
166  try:
167    nr_mask = OPTIONAL_CALLBACKS[func.__name__][0]
168  except KeyError:
169    raise ValueError(
170      '@noreply applied to non-optional method: '+func.__name__)
171  @wraps(func)
172  def wrapper(self,*args):
173    rc = func(self,*args)
174    if self._protocol & nr_mask:
175      if rc != CONTINUE:
176        raise RuntimeError('%s return code must be CONTINUE with @noreply'
177	  % func.__name__)
178      return NOREPLY
179    return rc
180  wrapper.milter_protocol = nr_mask
181  return wrapper
182
183## Function decorator to set macros used in a callback.
184# By default, the MTA sends all macros defined for a callback.
185# If some or all of these are unused, the bandwidth can be saved
186# by listing the ones that are used.
187# @since 1.0.2
188def symlist(*syms):
189  if len(syms) > 5:
190    raise ValueError('@symlist limited to 5 macros by MTA: '+func.__name__)
191  def setsyms(func):
192    if func.__name__ not in MACRO_CALLBACKS:
193      raise ValueError('@symlist applied to non-symlist method: '+func.__name__)
194    func._symlist = syms
195    return func
196  return setsyms
197
198## Disabled action exception.
199# set_flags() can tell the MTA that this application will not use certain
200# features (such as CHGFROM).  This can also be negotiated for each
201# connection in the negotiate callback.  If the application then calls
202# the feature anyway via an instance method, this exception is
203# thrown.
204# @since 0.9.2
205class DisabledAction(RuntimeError):
206  pass
207
208## A do "nothing" Milter base class representing an SMTP connection.
209#
210# Python milters should derive from this class
211# unless they are using the low level milter module directly.
212#
213# Most of the methods are either "actions" or "callbacks".  Callbacks
214# are invoked by the MTA at certain points in the SMTP protocol.  For
215# instance when the HELO command is seen, the MTA calls the helo
216# callback before returning a response code.  All callbacks must
217# return one of these constants: CONTINUE, TEMPFAIL, REJECT, ACCEPT,
218# DISCARD, SKIP.  The NOREPLY response is supplied automatically by
219# the @@noreply decorator if negotiation with the MTA is successful.
220# @@noreply and @@nocallback methods should return CONTINUE for two reasons:
221# the MTA may not support negotiation, and the class may be running in a test
222# harness.
223#
224# Optional callbacks are disabled with the @@nocallback decorator, and
225# automatically reenabled when overridden.  Disabled callbacks should
226# still return CONTINUE for testing and MTAs that do not support
227# negotiation.
228
229# Each SMTP connection to the MTA calls the factory method you provide to
230# create an instance derived from this class.  This is typically the
231# constructor for a class derived from Base.  The _setctx() method attaches
232# the instance to the low level milter.milterContext object.  When the SMTP
233# connection terminates, the close callback is called, the low level connection
234# object is destroyed, and this normally causes instances of this class to be
235# garbage collected as well.  The close() method should release any global
236# resources held by instances.
237# @since 0.9.2
238class Base(object):
239  "The core class interface to the %milter module."
240
241  ## Attach this Milter to the low level milter.milterContext object.
242  def _setctx(self,ctx):
243    ## The low level @ref milter.milterContext object.
244    self._ctx = ctx
245    ## A bitmask of actions this connection has negotiated to use.
246    # By default, all actions are enabled.  High throughput milters
247    # may want to disable unused actions to increase efficiency.
248    # Some optional actions may be disabled by calling milter.set_flags(), or
249    # by overriding the negotiate callback.  The bits include:
250    # <code>ADDHDRS,CHGBODY,MODBODY,ADDRCPT,ADDRCPT_PAR,DELRCPT
251    #  CHGHDRS,QUARANTINE,CHGFROM,SETSYMLIST</code>.
252    # The <code>Milter.CURR_ACTS</code> bitmask is all actions
253    # known when the milter module was compiled.
254    # Application code can also inspect this field to determine
255    # which actions are available.  This is especially useful in
256    # generic library code designed to work in multiple milters.
257    # @since 0.9.2
258    #
259    self._actions = CURR_ACTS         # all actions enabled by default
260    ## A bitmask of protocol options this connection has negotiated.
261    # An application may inspect this
262    # variable to determine which protocol steps are supported.  Options
263    # of interest to applications: the SKIP result code is allowed
264    # only if the P_SKIP bit is set, rejected recipients are passed to the
265    # %milter application only if the P_RCPT_REJ bit is set, and
266    # header values are sent and received with leading spaces (in the
267    # continuation lines) intact if the P_HDR_LEADSPC bit is set (so
268    # that the application can customize indenting).
269    #
270    # The P_N* bits should be negotiated via the @@noreply and @@nocallback
271    # method decorators, and P_RCPT_REJ, P_HDR_LEADSPC should
272    # be enabled using the enable_protocols class decorator.
273    #
274    # The bits include: <code>
275    # P_RCPT_REJ P_NR_CONN P_NR_HELO P_NR_MAIL P_NR_RCPT P_NR_DATA P_NR_UNKN
276    # P_NR_EOH P_NR_BODY P_NR_HDR P_NOCONNECT P_NOHELO P_NOMAIL P_NORCPT
277    # P_NODATA P_NOUNKNOWN P_NOEOH P_NOBODY P_NOHDRS P_HDR_LEADSPC P_SKIP
278    # </code> (all under the Milter namespace).
279    # @since 0.9.2
280    self._protocol = 0                # no protocol options by default
281    if ctx:
282      ctx.setpriv(self)
283
284  ## Defined by subclasses to write log messages.
285  def log(self,*msg): pass
286  ## Called for each connection to the MTA.  Called by the
287  # <a href="milter_api/xxfi_connect.html">
288  # xxfi_connect</a> callback.
289  # The <code>hostname</code> provided by the local MTA is either
290  # the PTR name or the IP in the form "[1.2.3.4]" if no PTR is available.
291  # The format of hostaddr depends on the socket family:
292  # <dl>
293  # <dt><code>socket.AF_INET</code>
294  # <dd>A tuple of (IP as string in dotted quad form, integer port)
295  # <dt><code>socket.AF_INET6</code>
296  # <dd>A tuple of (IP as a string in standard representation,
297  # integer port, integer flow info, integer scope id)
298  # <dt><code>socket.AF_UNIX</code>
299  # <dd>A string with the socketname
300  # </dl>
301  # To vary behavior based on what port the client connected to,
302  # for example skipping blacklist checks for port 587 (which must
303  # be authenticated), use @link #getsymval getsymval('{daemon_port}') @endlink.
304  # The <code>{daemon_port}</code> macro must be enabled in sendmail.cf
305  # <pre>
306  # O Milter.macros.connect=j, _, {daemon_name}, {daemon_port}, {if_name}, {if_addr}
307  # </pre>
308  # or sendmail.mc
309  # <pre>
310  # define(`confMILTER_MACROS_CONNECT', ``j, _, {daemon_name}, {daemon_port}, {if_name}, {if_addr}'')dnl
311  # </pre>
312  # @param hostname the PTR name or bracketed IP of the SMTP client
313  # @param family <code>socket.AF_INET</code>, <code>socket.AF_INET6</code>,
314  #     or <code>socket.AF_UNIX</code>
315  # @param hostaddr a tuple or string with peer IP or socketname
316  @nocallback
317  def connect(self,hostname,family,hostaddr): return CONTINUE
318  ## Called when the SMTP client says HELO.
319  # Returning REJECT prevents progress until a valid HELO is provided;
320  # this almost always results in terminating the connection.
321  @nocallback
322  def hello(self,hostname): return CONTINUE
323  ## Called when the SMTP client says MAIL FROM. Called by the
324  # <a href="milter_api/xxfi_envfrom.html">
325  # xxfi_envfrom</a> callback.
326  # Returning REJECT rejects the message, but not the connection.
327  # The sender is the "envelope" from as defined by
328  # <a href="http://tools.ietf.org/html/rfc5321">RFC 5321</a>.
329  # For the From: header (author) defined in
330  # <a href="http://tools.ietf.org/html/rfc5322">RFC 5322</a>,
331  # see @link #header the header callback @endlink.
332  @nocallback
333  def envfrom(self,f,*str): return CONTINUE
334  ## Called when the SMTP client says RCPT TO. Called by the
335  # <a href="milter_api/xxfi_envrcpt.html">
336  # xxfi_envrcpt</a> callback.
337  # Returning REJECT rejects the current recipient, not the entire message.
338  # The recipient is the "envelope" recipient as defined by
339  # <a href="http://tools.ietf.org/html/rfc5321">RFC 5321</a>.
340  # For recipients defined in
341  # <a href="http://tools.ietf.org/html/rfc5322">RFC 5322</a>,
342  # for example To: or Cc:, see @link #header the header callback @endlink.
343  @nocallback
344  def envrcpt(self,to,*str): return CONTINUE
345  ## Called when the SMTP client says DATA.
346  # Returning REJECT rejects the message without wasting bandwidth
347  # on the unwanted message.
348  # @since 0.9.2
349  @nocallback
350  def data(self): return CONTINUE
351  ## Called for each header field in the message body.
352  @nocallback
353  def header(self,field,value): return CONTINUE
354  ## Called at the blank line that terminates the header fields.
355  @nocallback
356  def eoh(self): return CONTINUE
357  ## Called to supply the body of the message to the Milter by chunks.
358  # @param blk a block of message bytes
359  @nocallback
360  def body(self,blk): return CONTINUE
361  ## Called when the SMTP client issues an unknown command.
362  # @param cmd the unknown command
363  # @since 0.9.2
364  @nocallback
365  def unknown(self,cmd): return CONTINUE
366  ## Called at the end of the message body.
367  # Most of the message manipulation actions can only take place from
368  # the eom callback.
369  def eom(self): return CONTINUE
370  ## Called when the connection is abnormally terminated.
371  # The close callback is still called also.
372  def abort(self): return CONTINUE
373  ## Called when the connection is closed.
374  def close(self): return CONTINUE
375
376  ## Return mask of SMFIP_N* protocol option bits to clear for this class
377  # The @@nocallback and @@noreply decorators set the
378  # <code>milter_protocol</code> function attribute to the protocol mask bit to
379  # pass to libmilter, causing that callback or its reply to be skipped.
380  # Overriding a method creates a new function object, so that
381  # <code>milter_protocol</code> defaults to 0.
382  # Libmilter passes the protocol bits that the current MTA knows
383  # how to skip.  We clear the ones we don't want to skip.
384  # The negation is somewhat mind bending, but it is simple.
385  # @since 0.9.2
386  @classmethod
387  def protocol_mask(klass):
388    try:
389      return klass._protocol_mask
390    except AttributeError:
391      p = P_RCPT_REJ | P_HDR_LEADSPC    # turn these new features off by default
392      for func,(nr,nc) in OPTIONAL_CALLBACKS.items():
393        func = getattr(klass,func)
394        ca = getattr(func,'milter_protocol',0)
395        #print(func,hex(nr),hex(nc),hex(ca))
396        p |= (nr|nc) & ~ca
397      klass._protocol_mask = p
398      return p
399
400  ## Negotiate milter protocol options.  Called by the
401  # <a href="milter_api/xxfi_negotiate.html">
402  # xffi_negotiate</a> callback.  This is an advanced callback,
403  # do not override unless you know what you are doing.  Most
404  # negotiation can be done simply by using the supplied
405  # class and function decorators.
406  # Options are passed as
407  # a list of 4 32-bit ints which can be modified and are passed
408  # back to libmilter on return.
409  # Default negotiation sets P_NO* and P_NR* for callbacks
410  # marked @@nocallback and @@noreply respectively, leaves all
411  # actions enabled, and enables Milter.SKIP.  The @@enable_protocols
412  # class decorator can customize which protocol steps are implemented.
413  # @param opts a modifiable list of 4 ints with negotiated options
414  # @since 0.9.2
415  def negotiate(self,opts):
416    try:
417      self._actions,p,f1,f2 = opts
418      for func,stage in MACRO_CALLBACKS.items():
419        func = getattr(self,func)
420        syms = getattr(func,'_symlist',None)
421        if syms is not None:
422          self.setsymlist(stage,*syms)
423      opts[1] = self._protocol = p & ~self.protocol_mask()
424      opts[2] = 0
425      opts[3] = 0
426      #self.log("Negotiated:",opts)
427    except Exception as x:
428      # don't change anything if something went wrong
429      return ALL_OPTS
430    return CONTINUE
431
432  # Milter methods which can be invoked from most callbacks
433
434  ## Return the value of an MTA macro.  Sendmail macro names
435  # are either single chars (e.g. "j") or multiple chars enclosed
436  # in braces (e.g. "{auth_type}").  Macro names are MTA dependent.
437  # See <a href="milter_api/smfi_getsymval.html">
438  # smfi_getsymval</a> for default sendmail macros.
439  # @param sym the macro name
440  def getsymval(self,sym):
441    return self._ctx.getsymval(sym)
442
443  ## Set the SMTP reply code and message.
444  # If the MTA does not support setmlreply, then only the
445  # first msg line is used.  Any '%%' in a message line
446  # must be doubled, or libmilter will silently ignore the setreply.
447  # Beginning with 0.9.6, we test for that case and throw ValueError to avoid
448  # head scratching.  What will <i>really</i> irritate you, however,
449  # is that if you carefully double any '%%', your message will be
450  # sent - but with the '%%' still doubled!
451  # See <a href="milter_api/smfi_setreply.html">
452  # smfi_setreply</a> for more information.
453  # @param rcode The three-digit (RFC 821/2821) SMTP reply code as a string.
454  # rcode cannot be None, and <b>must be a valid 4XX or 5XX reply code</b>.
455  # @param xcode The extended (RFC 1893/2034) reply code. If xcode is None,
456  # no extended code is used. Otherwise, xcode must conform to RFC 1893/2034.
457  # @param msg The text part of the SMTP reply. If msg is None,
458  # an empty message is used.
459  # @param ml  Optional additional message lines.
460  def setreply(self,rcode,xcode=None,msg=None,*ml):
461    for m in (msg,)+ml:
462      if 1 in [len(s)&1 for s in R.findall(m)]:
463        raise ValueError("'%' must be doubled: "+m)
464    return self._ctx.setreply(rcode,xcode,msg,*ml)
465
466  ## Tell the MTA which macro names will be used.
467  # This information can reduce the size of messages received from sendmail,
468  # and hence could reduce bandwidth between sendmail and your milter where
469  # that is a factor.  The <code>Milter.SETSYMLIST</code> action flag must be
470  # set.  The protocol stages are M_CONNECT, M_HELO, M_ENVFROM, M_ENVRCPT,
471  # M_DATA, M_EOM, M_EOH.
472  #
473  # May only be called from negotiate callback.  Hence, this is an advanced
474  # feature.  Use the @@symlist function decorator to conviently set
475  # the macros used by a callback.
476  # @since 0.9.8, previous version was misspelled!
477  # @param stage the protocol stage to set to macro list for,
478  # one of the M_* constants defined in Milter
479  # @param macros space separated and/or lists of strings
480  def setsymlist(self,stage,*macros):
481    if not self._actions & SETSYMLIST: raise DisabledAction("SETSYMLIST")
482    if len(macros) > 5:
483      raise ValueError('setsymlist limited to 5 macros by MTA')
484    a = []
485    for m in macros:
486      try:
487        m = m.encode('utf8')
488      except: pass
489      try:
490        m = m.split(b' ')
491        a += m
492      except: pass
493    return self._ctx.setsymlist(stage,b' '.join(a))
494
495  # Milter methods which can only be called from eom callback.
496
497  ## Add a mail header field.
498  # Calls <a href="milter_api/smfi_addheader.html">
499  # smfi_addheader</a>.
500  # The <code>Milter.ADDHDRS</code> action flag must be set.
501  #
502  # May be called from eom callback only.
503  # @param field        the header field name
504  # @param value        the header field value
505  # @param idx header field index from the top of the message to insert at
506  # @throws DisabledAction if ADDHDRS is not enabled
507  def addheader(self,field,value,idx=-1):
508    if not self._actions & ADDHDRS: raise DisabledAction("ADDHDRS")
509    return self._ctx.addheader(field,value,idx)
510
511  ## Change the value of a mail header field.
512  # Calls <a href="milter_api/smfi_chgheader.html">
513  # smfi_chgheader</a>.
514  # The <code>Milter.CHGHDRS</code> action flag must be set.
515  #
516  # May be called from eom callback only.
517  # @param field the name of the field to change
518  # @param idx index of the field to change when there are multiple instances
519  # @param value the new value of the field
520  # @throws DisabledAction if CHGHDRS is not enabled
521  def chgheader(self,field,idx,value):
522    if not self._actions & CHGHDRS: raise DisabledAction("CHGHDRS")
523    return self._ctx.chgheader(field,idx,value)
524
525  ## Add a recipient to the message.
526  # Calls <a href="milter_api/smfi_addrcpt.html">
527  # smfi_addrcpt</a>.
528  # If no corresponding mail header is added, this is like a Bcc.
529  # The syntax of the recipient is the same as used in the SMTP
530  # RCPT TO command (and as delivered to the envrcpt callback), for example
531  # "self.addrcpt('<foo@example.com>')".
532  # The <code>Milter.ADDRCPT</code> action flag must be set.
533  # If the optional <code>params</code> argument is used, then
534  # the <code>Milter.ADDRCPT_PAR</code> action flag must be set.
535  #
536  # May be called from eom callback only.
537  # @param rcpt the message recipient
538  # @param params an optional list of ESMTP parameters
539  # @throws DisabledAction if ADDRCPT or ADDRCPT_PAR is not enabled
540  def addrcpt(self,rcpt,params=None):
541    if not self._actions & ADDRCPT: raise DisabledAction("ADDRCPT")
542    if params and not self._actions & ADDRCPT_PAR:
543        raise DisabledAction("ADDRCPT_PAR")
544    return self._ctx.addrcpt(rcpt,params)
545  ## Delete a recipient from the message.
546  # Calls <a href="milter_api/smfi_delrcpt.html">
547  # smfi_delrcpt</a>.
548  # The recipient should match one passed to the envrcpt callback.
549  # The <code>Milter.DELRCPT</code> action flag must be set.
550  #
551  # May be called from eom callback only.
552  # @param rcpt the message recipient to delete
553  # @throws DisabledAction if DELRCPT is not enabled
554  def delrcpt(self,rcpt):
555    if not self._actions & DELRCPT: raise DisabledAction("DELRCPT")
556    return self._ctx.delrcpt(rcpt)
557
558  ## Replace the message body.
559  # Calls <a href="milter_api/smfi_replacebody.html">
560  # smfi_replacebody</a>.
561  # The entire message body must be replaced.
562  # Call repeatedly with blocks of data until the entire body is transferred.
563  # The <code>Milter.MODBODY</code> action flag must be set.
564  #
565  # May be called from eom callback only.
566  # @param body a chunk of body data
567  # @throws DisabledAction if MODBODY is not enabled
568  def replacebody(self,body):
569    if not self._actions & MODBODY: raise DisabledAction("MODBODY")
570    return self._ctx.replacebody(body)
571
572  ## Change the SMTP envelope sender address.
573  # Calls <a href="milter_api/smfi_chgfrom.html">
574  # smfi_chgfrom</a>.
575  # The syntax of the sender is that same as used in the SMTP
576  # MAIL FROM command (and as delivered to the envfrom callback),
577  # for example <code>self.chgfrom('<bar@example.com>')</code>.
578  # The <code>Milter.CHGFROM</code> action flag must be set.
579  #
580  # May be called from eom callback only.
581  # @since 0.9.1
582  # @param sender the new sender address
583  # @param params an optional list of ESMTP parameters
584  # @throws DisabledAction if CHGFROM is not enabled
585  def chgfrom(self,sender,params=None):
586    if not self._actions & CHGFROM: raise DisabledAction("CHGFROM")
587    return self._ctx.chgfrom(sender,params)
588
589  ## Quarantine the message.
590  # Calls <a href="milter_api/smfi_quarantine.html">
591  # smfi_quarantine</a>.
592  # When quarantined, a message goes into the mailq as if to be delivered,
593  # but delivery is deferred until the message is unquarantined.
594  # The <code>Milter.QUARANTINE</code> action flag must be set.
595  #
596  # May be called from eom callback only.
597  # @param reason a string describing the reason for quarantine
598  # @throws DisabledAction if QUARANTINE is not enabled
599  def quarantine(self,reason):
600    if not self._actions & QUARANTINE: raise DisabledAction("QUARANTINE")
601    return self._ctx.quarantine(reason)
602
603  ## Tell the MTA to wait a bit longer.
604  # Calls <a href="milter_api/smfi_progress.html">
605  # smfi_progress</a>.
606  # Resets timeouts in the MTA that detect a "hung" milter.
607  def progress(self):
608    return self._ctx.progress()
609
610## A logging but otherwise do nothing Milter base class.
611# This is included for compatibility with previous versions of pymilter.
612# The logging callbacks are marked @@noreply.
613class Milter(Base):
614  "A simple class interface to the milter module."
615
616  ## Provide simple logging to sys.stdout
617  def log(self,*msg):
618    print('Milter:',end=None)
619    for i in msg: print(i,end=None)
620    print()
621
622  @noreply
623  def connect(self,hostname,family,hostaddr):
624    "Called for each connection to sendmail."
625    self.log("connect from %s at %s" % (hostname,hostaddr))
626    return CONTINUE
627
628  @noreply
629  def hello(self,hostname):
630    "Called after the HELO command."
631    self.log("hello from %s" % hostname)
632    return CONTINUE
633
634  @noreply
635  def envfrom(self,f,*str):
636    """Called to begin each message.
637    f -> string		message sender
638    str -> tuple	additional ESMTP parameters
639    """
640    self.log("mail from",f,str)
641    return CONTINUE
642
643  @noreply
644  def envrcpt(self,to,*str):
645    "Called for each message recipient."
646    self.log("rcpt to",to,str)
647    return CONTINUE
648
649  @noreply
650  def header(self,field,value):
651    "Called for each message header."
652    self.log("%s: %s" % (field,value))
653    return CONTINUE
654
655  @noreply
656  def eoh(self):
657    "Called after all headers are processed."
658    self.log("eoh")
659    return CONTINUE
660
661  def eom(self):
662    "Called at the end of message."
663    self.log("eom")
664    return CONTINUE
665
666  def abort(self):
667    "Called if the connection is terminated abnormally."
668    self.log("abort")
669    return CONTINUE
670
671  def close(self):
672    "Called at the end of connection, even if aborted."
673    self.log("close")
674    return CONTINUE
675
676## The milter connection factory
677# This factory method is called for each connection to create the
678# python object that tracks the connection.  It should return
679# an object derived from Milter.Base.
680#
681# Note that since python is dynamic, this variable can be changed while
682# the milter is running: for instance, to a new subclass based on a
683# change in configuration.
684factory = Milter
685
686## @private
687# @brief Connect context to connection instance and return enabled callbacks.
688def negotiate_callback(ctx,opts):
689  m = factory()
690  m._setctx(ctx)
691  return m.negotiate(opts)
692
693## @private
694# @brief Connect context if needed and invoke connect method.
695def connect_callback(ctx,hostname,family,hostaddr,nr_mask=P_NR_CONN):
696  m = ctx.getpriv()
697  if not m:
698    # If not already created (because the current MTA doesn't support
699    # xmfi_negotiate), create the connection object.
700    m = factory()
701    m._setctx(ctx)
702  return m.connect(hostname,family,hostaddr)
703
704## @private
705# @brief Disconnect milterContext and call close method.
706def close_callback(ctx):
707  m = ctx.getpriv()
708  if not m: return CONTINUE
709  try:
710    rc = m.close()
711  finally:
712    m._setctx(None)	# release milterContext
713  return rc
714
715## Convert ESMTP parameters with values to a keyword dictionary.
716# @deprecated You probably want Milter.param2dict instead.
717def dictfromlist(args):
718  "Convert ESMTP parms with values to keyword dictionary."
719  kw = {}
720  for s in args:
721    pos = s.find('=')
722    if pos > 0:
723      kw[s[:pos].upper()] = s[pos+1:]
724  return kw
725
726## Convert ESMTP parm list to keyword dictionary.
727# Params with no value are set to None in the dictionary.
728# @since 0.9.3
729# @param str list of param strings of the form "NAME" or "NAME=VALUE"
730# @return a dictionary of ESMTP param names and values
731def param2dict(str):
732  "Convert ESMTP parm list to keyword dictionary."
733  pairs = [x.split('=',1) for x in str]
734  for e in pairs:
735    if len(e) < 2: e.append(None)
736  return dict([(k.upper(),v) for k,v in pairs])
737
738def envcallback(c,args):
739  """Call function c with ESMTP parms converted to keyword parameters.
740  Can be used in the envfrom and/or envrcpt callbacks to process
741  ESMTP parameters as python keyword parameters."""
742  kw = {}
743  pargs = [args[0]]
744  for s in args[1:]:
745    pos = s.find('=')
746    if pos > 0:
747      kw[s[:pos].upper()] = s[pos+1:]
748    else:
749      pargs.append(s)
750  return c(*pargs,**kw)
751
752## Run the %milter.
753# @param name the name of the %milter known to the MTA
754# @param socketname the socket to be passed to milter.setconn()
755# @param timeout the time in secs the MTA should wait for a response before
756#	considering this %milter dead
757def runmilter(name,socketname,timeout = 0,rmsock=True):
758
759  # The default flags set include everything
760  # milter.set_flags(milter.ADDHDRS)
761  milter.set_connect_callback(connect_callback)
762  milter.set_helo_callback(lambda ctx, host: ctx.getpriv().hello(host))
763  # For envfrom and envrcpt, we would like to convert ESMTP parms to keyword
764  # parms, but then all existing users would have to include **kw to accept
765  # arbitrary keywords without crashing.  We do provide envcallback and
766  # dictfromlist to make parsing the ESMTP args convenient.
767  milter.set_envfrom_callback(lambda ctx,*str: ctx.getpriv().envfrom(*str))
768  milter.set_envrcpt_callback(lambda ctx,*str: ctx.getpriv().envrcpt(*str))
769  milter.set_header_callback(lambda ctx,fld,val: ctx.getpriv().header(fld,val))
770  milter.set_eoh_callback(lambda ctx: ctx.getpriv().eoh())
771  milter.set_body_callback(lambda ctx,chunk: ctx.getpriv().body(chunk))
772  milter.set_eom_callback(lambda ctx: ctx.getpriv().eom())
773  milter.set_abort_callback(lambda ctx: ctx.getpriv().abort())
774  milter.set_close_callback(close_callback)
775
776  milter.setconn(socketname)
777  if timeout > 0: milter.settimeout(timeout)
778  # disable negotiate callback if runtime version < (1,0,1)
779  ncb = negotiate_callback
780  if milter.getversion() < (1,0,1):
781    ncb = None
782  # The name *must* match the X line in sendmail.cf (supposedly)
783  milter.register(name,
784        data=lambda ctx: ctx.getpriv().data(),
785        unknown=lambda ctx,cmd: ctx.getpriv().unknown(cmd),
786        negotiate=ncb
787  )
788
789  # We remove the socket here by default on the assumption that you will be
790  # starting this filter before sendmail.  If sendmail is not running and the
791  # socket already exists, libmilter will throw a warning.  If sendmail is
792  # running, this is still safe if there are no messages currently being
793  # processed.  It's safer to shutdown sendmail, kill the filter process,
794  # restart the filter, and then restart sendmail.
795  milter.opensocket(rmsock)
796  start_seq = _seq
797  try:
798    milter.main()
799  except milter.error:
800    if start_seq == _seq: raise	# couldn't start
801    # milter has been running for a while, but now it can't start new threads
802    raise milter.error("out of thread resources")
803
804__all__ = globals().copy()
805for priv in ('os','milter','thread','factory','_seq','_seq_lock','__version__'):
806  del __all__[priv]
807__all__ = __all__.keys()
808
809## @example milter-template.py
810## @example milter-nomix.py
811#
812