~sambuddhabasu1/mailman/fix_mailman_run_error

7199 by Barry Warsaw
Bump copyright years.
1
# Copyright (C) 1998-2013 by the Free Software Foundation, Inc.
1713 by bwarsaw
New pipeline delivery module
2
#
6643 by Barry Warsaw
Upgrade to GPLv3.
3
# This file is part of GNU Mailman.
4
#
5
# GNU Mailman is free software: you can redistribute it and/or modify it under
6
# the terms of the GNU General Public License as published by the Free
7
# Software Foundation, either version 3 of the License, or (at your option)
8
# any later version.
9
#
10
# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
11
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13
# more details.
14
#
15
# You should have received a copy of the GNU General Public License along with
16
# GNU Mailman.  If not, see <http://www.gnu.org/licenses/>.
1713 by bwarsaw
New pipeline delivery module
17
6504.1.12 by Barry Warsaw
Convert the CookHeaders tests in test_handlers to using doctests, split up
18
"""Cook a message's headers."""
1713 by bwarsaw
New pipeline delivery module
19
7152 by Barry Warsaw
General code cleanup.
20
from __future__ import absolute_import, print_function, unicode_literals
6671 by Barry Warsaw
Several important cleanups.
21
6592 by Barry Warsaw
Reorganize the Handler architecture to a pipeline architecture with plugins.
22
__metaclass__ = type
6643.2.5 by Barry Warsaw
Move core Mailman modules to the new mailman.core package. Functionality
23
__all__ = [
24
    'CookHeaders',
25
    ]
6592 by Barry Warsaw
Reorganize the Handler architecture to a pipeline architecture with plugins.
26
27
1713 by bwarsaw
New pipeline delivery module
28
import re
2736 by bwarsaw
Conversion to mimelib.
29
6643.2.5 by Barry Warsaw
Move core Mailman modules to the new mailman.core package. Functionality
30
from email.errors import HeaderParseError
31
from email.header import Header, decode_header, make_header
32
from email.utils import parseaddr, formataddr, getaddresses
7152 by Barry Warsaw
General code cleanup.
33
from zope.interface import implementer
3918 by bwarsaw
process(): Reworked Reply-To: munging so list owners can establish
34
6810.1.1 by Barry Warsaw
Factor out most of the i18n subsystem and convert to using the flufl.i18n
35
from mailman.core.i18n import _
6661 by Barry Warsaw
Remove the mailman.interface magic. Use the more specific interface imports.
36
from mailman.interfaces.handler import IHandler
37
from mailman.interfaces.mailinglist import Personalization, ReplyToMunging
6625 by Barry Warsaw
mailman.Version -> mailman.version
38
from mailman.version import VERSION
6618 by Barry Warsaw
More fixes to get the basic end-to-end delivery mechanisms working.
39
1713 by bwarsaw
New pipeline delivery module
40
3918 by bwarsaw
process(): Reworked Reply-To: munging so list owners can establish
41
COMMASPACE = ', '
4765 by bwarsaw
process(), prefix_subject(): Ben Gertzfield's patch (refactored by
42
MAXLINELEN = 78
2689 by bwarsaw
process(): Get rid of the string module in favor of string methods.
43
6158 by tkikuchi
back porting from 2.1.6
44
nonascii = re.compile('[^\s!-~]')
45
6334 by bwarsaw
Remove most uses of the types module, in favor of isinstance checks against
46
47

6158 by tkikuchi
back porting from 2.1.6
48
def uheader(mlist, s, header_name=None, continuation_ws='\t', maxlinelen=None):
6859 by Barry Warsaw
Pick lint. Down to 4137 lines of warnings.
49
    """Get the charset to encode the string in.
50
51
    Then search if there is any non-ascii character is in the string.  If
52
    there is and the charset is us-ascii then we use iso-8859-1 instead.  If
53
    the string is ascii only we use 'us-ascii' if another charset is
54
    specified.
55
    """
6686 by Barry Warsaw
Much clean up of the language code, though more can be done. Factor out the
56
    charset = mlist.preferred_language.charset
6158 by tkikuchi
back porting from 2.1.6
57
    if nonascii.search(s):
58
        # use list charset but ...
59
        if charset == 'us-ascii':
60
            charset = 'iso-8859-1'
61
    else:
62
        # there is no nonascii so ...
63
        charset = 'us-ascii'
64
    return Header(s, charset, maxlinelen, header_name, continuation_ws)
5484 by bwarsaw
process(): Patch #625482 by Tokio Kikuchi to properly MIME encode the
65
5240 by bwarsaw
Better support for "funny" characters in subject prefixes.
66
67

2121 by bwarsaw
Many changes to make message delivery more robust in the face of
68
def process(mlist, msg, msgdata):
6859 by Barry Warsaw
Pick lint. Down to 4137 lines of warnings.
69
    """Process the headers of the message."""
2479 by bwarsaw
process(): To finally break -admin autoreply loops, we need to add the
70
    # Set the "X-Ack: no" header if noack flag is set.
71
    if msgdata.get('noack'):
2736 by bwarsaw
Conversion to mimelib.
72
        del msg['x-ack']
2479 by bwarsaw
process(): To finally break -admin autoreply loops, we need to add the
73
        msg['X-Ack'] = 'no'
1808 by bwarsaw
First, because this module is going to modify the Sender: field for
74
    # Because we're going to modify various important headers in the email
2121 by bwarsaw
Many changes to make message delivery more robust in the face of
75
    # message, we want to save some of the information in the msgdata
76
    # dictionary for later.  Specifically, the sender header will get waxed,
77
    # but we need it for the Acknowledge module later.
6682 by Barry Warsaw
Move mailman.Message to mailman.email.Message. Rename Message.get_sender() to
78
    msgdata['original_sender'] = msg.sender
2736 by bwarsaw
Conversion to mimelib.
79
    # VirginRunner sets _fasttrack for internally crafted messages.
80
    fasttrack = msgdata.get('_fasttrack')
2121 by bwarsaw
Many changes to make message delivery more robust in the face of
81
    if not msgdata.get('isdigest') and not fasttrack:
6158 by tkikuchi
back porting from 2.1.6
82
        try:
83
            prefix_subject(mlist, msg, msgdata)
84
        except (UnicodeError, ValueError):
85
            # TK: Sometimes subject header is not MIME encoded for 8bit
86
            # simply abort prefixing.
87
            pass
1713 by bwarsaw
New pipeline delivery module
88
    # Add Precedence: and other useful headers.  None of these are standard
89
    # and finding information on some of them are fairly difficult.  Some are
90
    # just common practice, and we'll add more here as they become necessary.
2736 by bwarsaw
Conversion to mimelib.
91
    # Good places to look are:
1713 by bwarsaw
New pipeline delivery module
92
    #
93
    # http://www.dsv.su.se/~jpalme/ietf/jp-ietf-home.html
2736 by bwarsaw
Conversion to mimelib.
94
    # http://www.faqs.org/rfcs/rfc2076.html
1713 by bwarsaw
New pipeline delivery module
95
    #
2736 by bwarsaw
Conversion to mimelib.
96
    # None of these headers are added if they already exist.  BAW: some
97
    # consider the advertising of this a security breach.  I.e. if there are
98
    # known exploits in a particular version of Mailman and we know a site is
99
    # using such an old version, they may be vulnerable.  It's too easy to
100
    # edit the code to add a configuration variable to handle this.
6504.1.12 by Barry Warsaw
Convert the CookHeaders tests in test_handlers to using doctests, split up
101
    if 'x-mailman-version' not in msg:
6625 by Barry Warsaw
mailman.Version -> mailman.version
102
        msg['X-Mailman-Version'] = VERSION
4927 by bwarsaw
process(): Use Precedence: list instead of bulk for mail list exploded
103
    # We set "Precedence: list" because this is the recommendation from the
104
    # sendmail docs, the most authoritative source of this header's semantics.
6504.1.12 by Barry Warsaw
Convert the CookHeaders tests in test_handlers to using doctests, split up
105
    if 'precedence' not in msg:
4927 by bwarsaw
process(): Use Precedence: list instead of bulk for mail list exploded
106
        msg['Precedence'] = 'list'
1987 by bwarsaw
process(): 'fastrack' => 'fasttrack'. Elaborate the Reply-To: munging
107
    # Reply-To: munging.  Do not do this if the message is "fast tracked",
2736 by bwarsaw
Conversion to mimelib.
108
    # meaning it is internally crafted and delivered to a specific user.  BAW:
109
    # Yuck, I really hate this feature but I've caved under the sheer pressure
3918 by bwarsaw
process(): Reworked Reply-To: munging so list owners can establish
110
    # of the (very vocal) folks want it.  OTOH, RFC 2822 allows Reply-To: to
111
    # be a list of addresses, so instead of replacing the original, simply
112
    # augment it.  RFC 2822 allows max one Reply-To: header so collapse them
113
    # if we're adding a value, otherwise don't touch it.  (Should we collapse
114
    # in all cases?)
2631 by bwarsaw
process(): "Fix" the Reply-To: header munging algorithm. If munging
115
    if not fasttrack:
5317 by bwarsaw
process(): Two important fixes. First, when adding Cc headers to
116
        # A convenience function, requires nested scopes.  pair is (name, addr)
117
        new = []
5009 by bwarsaw
process(): Implement a suggestion by David W. Tamkin on list-managers
118
        d = {}
5317 by bwarsaw
process(): Two important fixes. First, when adding Cc headers to
119
        def add(pair):
120
            lcaddr = pair[1].lower()
6504.1.12 by Barry Warsaw
Convert the CookHeaders tests in test_handlers to using doctests, split up
121
            if lcaddr in d:
5009 by bwarsaw
process(): Implement a suggestion by David W. Tamkin on list-managers
122
                return
5317 by bwarsaw
process(): Two important fixes. First, when adding Cc headers to
123
            d[lcaddr] = pair
124
            new.append(pair)
5009 by bwarsaw
process(): Implement a suggestion by David W. Tamkin on list-managers
125
        # List admin wants an explicit Reply-To: added
6504.1.12 by Barry Warsaw
Convert the CookHeaders tests in test_handlers to using doctests, split up
126
        if mlist.reply_goes_to_list == ReplyToMunging.explicit_header:
5312 by bwarsaw
process(): If the list is personalized, and the list's posting address
127
            add(parseaddr(mlist.reply_to_address))
3918 by bwarsaw
process(): Reworked Reply-To: munging so list owners can establish
128
        # If we're not first stripping existing Reply-To: then we need to add
129
        # the original Reply-To:'s to the list we're building up.  In both
130
        # cases we'll zap the existing field because RFC 2822 says max one is
131
        # allowed.
132
        if not mlist.first_strip_reply_to:
133
            orig = msg.get_all('reply-to', [])
5312 by bwarsaw
process(): If the list is personalized, and the list's posting address
134
            for pair in getaddresses(orig):
5009 by bwarsaw
process(): Implement a suggestion by David W. Tamkin on list-managers
135
                add(pair)
136
        # Set Reply-To: header to point back to this list.  Add this last
137
        # because some folks think that some MUAs make it easier to delete
138
        # addresses from the right than from the left.
6504.1.12 by Barry Warsaw
Convert the CookHeaders tests in test_handlers to using doctests, split up
139
        if mlist.reply_goes_to_list == ReplyToMunging.point_to_list:
6158 by tkikuchi
back porting from 2.1.6
140
            i18ndesc = uheader(mlist, mlist.description, 'Reply-To')
6504.1.12 by Barry Warsaw
Convert the CookHeaders tests in test_handlers to using doctests, split up
141
            add((str(i18ndesc), mlist.posting_address))
3918 by bwarsaw
process(): Reworked Reply-To: munging so list owners can establish
142
        del msg['reply-to']
5009 by bwarsaw
process(): Implement a suggestion by David W. Tamkin on list-managers
143
        # Don't put Reply-To: back if there's nothing to add!
5317 by bwarsaw
process(): Two important fixes. First, when adding Cc headers to
144
        if new:
5009 by bwarsaw
process(): Implement a suggestion by David W. Tamkin on list-managers
145
            # Preserve order
3918 by bwarsaw
process(): Reworked Reply-To: munging so list owners can establish
146
            msg['Reply-To'] = COMMASPACE.join(
5317 by bwarsaw
process(): Two important fixes. First, when adding Cc headers to
147
                [formataddr(pair) for pair in new])
148
        # The To field normally contains the list posting address.  However
5449 by bwarsaw
process(): Re-enable the Cc header hacking, but only if personalize ==
149
        # when messages are fully personalized, that header will get
150
        # overwritten with the address of the recipient.  We need to get the
151
        # posting address in one of the recipient headers or they won't be
152
        # able to reply back to the list.  It's possible the posting address
153
        # was munged into the Reply-To header, but if not, we'll add it to a
154
        # Cc header.  BAW: should we force it into a Reply-To header in the
155
        # above code?
6158 by tkikuchi
back porting from 2.1.6
156
        # Also skip Cc if this is an anonymous list as list posting address
157
        # is already in From and Reply-To in this case.
7215 by Barry Warsaw
* `bin/runner` command has been simplified and its command line options
158
        if (mlist.personalize is Personalization.full and
159
            mlist.reply_goes_to_list is not ReplyToMunging.point_to_list and
6504.1.12 by Barry Warsaw
Convert the CookHeaders tests in test_handlers to using doctests, split up
160
            not mlist.anonymous_list):
5449 by bwarsaw
process(): Re-enable the Cc header hacking, but only if personalize ==
161
            # Watch out for existing Cc headers, merge, and remove dups.  Note
162
            # that RFC 2822 says only zero or one Cc header is allowed.
163
            new = []
164
            d = {}
165
            for pair in getaddresses(msg.get_all('cc', [])):
166
                add(pair)
6158 by tkikuchi
back porting from 2.1.6
167
            i18ndesc = uheader(mlist, mlist.description, 'Cc')
6567 by Barry Warsaw
Remove the action.py module, move this to Mailman/interfaces/__init__.py.
168
            add((str(i18ndesc), mlist.posting_address))
5449 by bwarsaw
process(): Re-enable the Cc header hacking, but only if personalize ==
169
            del msg['Cc']
170
            msg['Cc'] = COMMASPACE.join([formataddr(pair) for pair in new])
4765 by bwarsaw
process(), prefix_subject(): Ben Gertzfield's patch (refactored by
171
172
173

5039 by bwarsaw
Allow postings gatewayed to Usenet to inhibit the Subject: field
174
def prefix_subject(mlist, msg, msgdata):
6859 by Barry Warsaw
Pick lint. Down to 4137 lines of warnings.
175
    """Maybe add a subject prefix.
7066 by Barry Warsaw
* Stop adding the X-BeenThere header.
176
6859 by Barry Warsaw
Pick lint. Down to 4137 lines of warnings.
177
    Add the subject prefix unless the message is a digest or is being fast
178
    tracked (e.g. internally crafted, delivered to a single user such as the
179
    list admin).
180
    """
6504.1.12 by Barry Warsaw
Convert the CookHeaders tests in test_handlers to using doctests, split up
181
    if not mlist.subject_prefix.strip():
6158 by tkikuchi
back porting from 2.1.6
182
        return
6504.1.12 by Barry Warsaw
Convert the CookHeaders tests in test_handlers to using doctests, split up
183
    prefix = mlist.subject_prefix
5737 by bwarsaw
prefix_subject(): Supply default argument '' to the get of the subject
184
    subject = msg.get('subject', '')
5723 by bwarsaw
Some fixes to retain the continuation whitespace on Subject: lines
185
    # Try to figure out what the continuation_ws is for the header
5750 by bwarsaw
prefix_subject(): If the subject is a Header instead of a string,
186
    if isinstance(subject, Header):
187
        lines = str(subject).splitlines()
188
    else:
189
        lines = subject.splitlines()
5723 by bwarsaw
Some fixes to retain the continuation whitespace on Subject: lines
190
    ws = '\t'
191
    if len(lines) > 1 and lines[1] and lines[1][0] in ' \t':
192
        ws = lines[1][0]
7139.1.1 by Barry Warsaw
* The `news` runner and queue has been renamed to the more accurate `nntp`.
193
    msgdata['original_subject'] = subject
6257 by bwarsaw
Port cleaning changes forward from 2.1-maint branch.
194
    # The subject may be multilingual but we take the first charset as major
195
    # one and try to decode.  If it is decodable, returned subject is in one
196
    # line and cset is properly set.  If fail, subject is mime-encoded and
197
    # cset is set as us-ascii.  See detail for ch_oneline() (CookHeaders one
198
    # line function).
6158 by tkikuchi
back porting from 2.1.6
199
    subject, cset = ch_oneline(subject)
6257 by bwarsaw
Port cleaning changes forward from 2.1-maint branch.
200
    # TK: Python interpreter has evolved to be strict on ascii charset code
201
    # range.  It is safe to use unicode string when manupilating header
202
    # contents with re module.  It would be best to return unicode in
203
    # ch_oneline() but here is temporary solution.
6251 by tkikuchi
Python interpreter has evolved to be strict on ascii charset range.
204
    subject = unicode(subject, cset)
6158 by tkikuchi
back porting from 2.1.6
205
    # If the subject_prefix contains '%d', it is replaced with the
206
    # mailing list sequential number.  Sequential number format allows
207
    # '%d' or '%05d' like pattern.
208
    prefix_pattern = re.escape(prefix)
209
    # unescape '%' :-<
210
    prefix_pattern = '%'.join(prefix_pattern.split(r'\%'))
211
    p = re.compile('%\d*d')
212
    if p.search(prefix, 1):
213
        # prefix have number, so we should search prefix w/number in subject.
214
        # Also, force new style.
215
        prefix_pattern = p.sub(r'\s*\d+\s*', prefix_pattern)
216
    subject = re.sub(prefix_pattern, '', subject)
6203 by tkikuchi
Finnish dialect of "Re:".
217
    rematch = re.match('((RE|AW|SV|VS)(\[\d+\])?:\s*)+', subject, re.I)
6158 by tkikuchi
back porting from 2.1.6
218
    if rematch:
219
        subject = subject[rematch.end():]
220
        recolon = 'Re:'
221
    else:
222
        recolon = ''
223
    # At this point, subject may become null if someone post mail with
224
    # subject: [subject prefix]
225
    if subject.strip() == '':
5779 by bwarsaw
prefix_subject(): Capture the no-subject header before we decode it.
226
        subject = _('(no subject)')
6686 by Barry Warsaw
Much clean up of the language code, though more can be done. Factor out the
227
        cset = mlist.preferred_language.charset
6158 by tkikuchi
back porting from 2.1.6
228
    # and substitute %d in prefix with post_id
229
    try:
230
        prefix = prefix % mlist.post_id
231
    except TypeError:
232
        pass
233
    # Get the header as a Header instance, with proper unicode conversion
6484 by tkikuchi
CookHeaders.py:
234
    if not recolon:
235
        h = uheader(mlist, prefix, 'Subject', continuation_ws=ws)
6158 by tkikuchi
back porting from 2.1.6
236
    else:
237
        h = uheader(mlist, prefix, 'Subject', continuation_ws=ws)
238
        h.append(recolon)
6257 by bwarsaw
Port cleaning changes forward from 2.1-maint branch.
239
    # TK: Subject is concatenated and unicode string.
6251 by tkikuchi
Python interpreter has evolved to be strict on ascii charset range.
240
    subject = subject.encode(cset, 'replace')
6158 by tkikuchi
back porting from 2.1.6
241
    h.append(subject, cset)
5240 by bwarsaw
Better support for "funny" characters in subject prefixes.
242
    del msg['subject']
243
    msg['Subject'] = h
6158 by tkikuchi
back porting from 2.1.6
244
    ss = uheader(mlist, recolon, 'Subject', continuation_ws=ws)
245
    ss.append(subject, cset)
246
    msgdata['stripped_subject'] = ss
247
6257 by bwarsaw
Port cleaning changes forward from 2.1-maint branch.
248
6158 by tkikuchi
back porting from 2.1.6
249

6297 by tkikuchi
ch_oneline(): Input string variable is overwritten. Also use 'utf-8'
250
def ch_oneline(headerstr):
6686 by Barry Warsaw
Much clean up of the language code, though more can be done. Factor out the
251
    # Decode header string in one line and convert into single charset.
252
    # Return (string, cset) tuple as check for failure.
6158 by tkikuchi
back porting from 2.1.6
253
    try:
6297 by tkikuchi
ch_oneline(): Input string variable is overwritten. Also use 'utf-8'
254
        d = decode_header(headerstr)
255
        # At this point, we should rstrip() every string because some
6158 by tkikuchi
back porting from 2.1.6
256
        # MUA deliberately add trailing spaces when composing return
257
        # message.
6664 by Barry Warsaw
Picking some (py)lint.
258
        d = [(s.rstrip(), c) for (s, c) in d]
6297 by tkikuchi
ch_oneline(): Input string variable is overwritten. Also use 'utf-8'
259
        # Find all charsets in the original header.  We use 'utf-8' rather
260
        # than using the first charset (in mailman 2.1.x) if multiple
261
        # charsets are used.
262
        csets = []
6664 by Barry Warsaw
Picking some (py)lint.
263
        for (s, c) in d:
6297 by tkikuchi
ch_oneline(): Input string variable is overwritten. Also use 'utf-8'
264
            if c and c not in csets:
265
                csets.append(c)
266
        if len(csets) == 0:
267
            cset = 'us-ascii'
268
        elif len(csets) == 1:
269
            cset = csets[0]
270
        else:
271
            cset = 'utf-8'
6158 by tkikuchi
back porting from 2.1.6
272
        h = make_header(d)
6297 by tkikuchi
ch_oneline(): Input string variable is overwritten. Also use 'utf-8'
273
        ustr = unicode(h)
6671 by Barry Warsaw
Several important cleanups.
274
        oneline = ''.join(ustr.splitlines())
6158 by tkikuchi
back porting from 2.1.6
275
        return oneline.encode(cset, 'replace'), cset
276
    except (LookupError, UnicodeError, ValueError, HeaderParseError):
277
        # possibly charset problem. return with undecoded string in one line.
6297 by tkikuchi
ch_oneline(): Input string variable is overwritten. Also use 'utf-8'
278
        return ''.join(headerstr.splitlines()), 'us-ascii'
6592 by Barry Warsaw
Reorganize the Handler architecture to a pipeline architecture with plugins.
279
280
281

7152 by Barry Warsaw
General code cleanup.
282
@implementer(IHandler)
6592 by Barry Warsaw
Reorganize the Handler architecture to a pipeline architecture with plugins.
283
class CookHeaders:
284
    """Modify message headers."""
285
286
    name = 'cook-headers'
287
    description = _('Modify message headers.')
288
289
    def process(self, mlist, msg, msgdata):
290
        """See `IHandler`."""
291
        process(mlist, msg, msgdata)