1 # Copyright (C) 2007 by the Free Software Foundation, Inc.
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
18 """Look for moderator pre-approval."""
20 __all__
= ['Approved']
25 from email
.iterators
import typed_subpart_iterator
26 from zope
.interface
import implements
28 from Mailman
.i18n
import _
29 from Mailman
.interfaces
import IRule
37 """Look for moderator pre-approval."""
41 description
= _('The message has a matching Approve or Approved header.')
43 def check(self
, mlist
, msg
, msgdata
):
45 # See if the message has an Approved or Approve header with a valid
46 # moderator password. Also look at the first non-whitespace line in
47 # the file to see if it looks like an Approved header.
49 password
= msg
.get('approved', msg
.get('approve', missing
))
50 if password
is missing
:
51 # Find the first text/plain part in the message
54 for part
in typed_subpart_iterator(msg
, 'text', 'plain'):
56 payload
= part
.get_payload(decode
=True)
57 if payload
is not None:
58 lines
= payload
.splitlines(True)
59 for lineno
, line
in enumerate(lines
):
60 if line
.strip() <> '':
63 header
, value
= line
.split(':', 1)
64 if header
.lower() in ('approved', 'approve'):
65 password
= value
.strip()
66 # Now strip the first line from the payload so the
67 # password doesn't leak.
69 reset_payload(part
, EMPTYSTRING
.join(lines
))
72 # Now try all the text parts in case it's
73 # multipart/alternative with the approved line in HTML or
74 # other text part. We make a pattern from the Approved line
75 # and delete it from all text/* parts in which we find it. It
76 # would be better to just iterate forward, but email
77 # compatability for pre Python 2.2 returns a list, not a true
80 # This will process all the multipart/alternative parts in the
81 # message as well as all other text parts. We shouldn't find
82 # the pattern outside the multipart/alternative parts, but if
83 # we do, it is probably best to delete it anyway as it does
84 # contain the password.
86 # Make a pattern to delete. We can't just delete a line
87 # because line of HTML or other fancy text may include
88 # additional message text. This pattern works with HTML. It
89 # may not work with rtf or whatever else is possible.
90 pattern
= header
+ ':(\s| )*' + re
.escape(password
)
91 for part
in typed_subpart_iterator(msg
, 'text'):
92 payload
= part
.get_payload(decode
=True)
93 if payload
is not None:
94 if re
.search(pattern
, payload
):
95 reset_payload(part
, re
.sub(pattern
, '', payload
))
99 return password
is not missing
and password
== mlist
.moderator_password
103 def reset_payload(part
, payload
):
104 # Set decoded payload maintaining content-type, charset, format and delsp.
105 charset
= part
.get_content_charset() or 'us-ascii'
106 content_type
= part
.get_content_type()
107 format
= part
.get_param('format')
108 delsp
= part
.get_param('delsp')
109 del part
['content-transfer-encoding']
110 del part
['content-type']
111 part
.set_payload(payload
, charset
)
112 part
.set_type(content_type
)
114 part
.set_param('Format', format
)
116 part
.set_param('DelSp', delsp
)