~msapiro/mailman/topics

« back to all changes in this revision

Viewing changes to Mailman/Handlers/Approve.py

  • Committer: Mark Sapiro
  • Date: 2014-12-03 23:47:23 UTC
  • mfrom: (1006.1.356 2.2)
  • Revision ID: mark@msapiro.net-20141203234723-3pcwx85xua4n84yx
Merged the 2.2 branch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 1998-2007 by the Free Software Foundation, Inc.
 
1
# Copyright (C) 1998-2011 by the Free Software Foundation, Inc.
2
2
#
3
3
# This program is free software; you can redistribute it and/or
4
4
# modify it under the terms of the GNU General Public License
39
39
 
40
40
NL = '\n'
41
41
 
 
42
def _(s):
 
43
    # message is translated when used.
 
44
    return s
 
45
REJECT = _("""Message rejected.
 
46
It appears that this message contains an HTML part with the
 
47
Approved: password line, but due to the way it is coded in the
 
48
HTML it can't be safely removed.
 
49
""")
 
50
del _
 
51
 
42
52
 
43
53
 
44
54
def process(mlist, msg, msgdata):
45
55
    # Short circuits
46
 
    if msgdata.get('approved'):
 
56
    # Do not short circuit. The problem is SpamDetect comes before Approve.
 
57
    # Suppose a message with an Approved: header is held by SpamDetect (or
 
58
    # any other handler that might come before Approve) and then approved
 
59
    # by a moderator. When the approved message reaches Approve in the
 
60
    # pipeline, we still need to remove the Approved: (pseudo-)header, so
 
61
    # we can't short circuit.
 
62
    #if msgdata.get('approved'):
47
63
        # Digests, Usenet postings, and some other messages come pre-approved.
48
64
        # TBD: we may want to further filter Usenet messages, so the test
49
65
        # above may not be entirely correct.
50
 
        return
 
66
        #return
51
67
    # See if the message has an Approved or Approve header with a valid
52
68
    # list-moderator, list-admin.  Also look at the first non-whitespace line
53
69
    # in the file to see if it looks like an Approved header.  We are
55
71
    # because we want to discourage the practice of sending the site admin
56
72
    # password through email in the clear.
57
73
    missing = []
58
 
    passwd = msg.get('approved', msg.get('approve', missing))
 
74
    for hdr in ('approved', 'approve', 'x-approved', 'x-approve'):
 
75
        passwd = msg.get(hdr, missing)
 
76
        if passwd is not missing:
 
77
            break
59
78
    if passwd is missing:
60
79
        # Find the first text/plain part in the message
61
80
        part = None
74
93
            if i >= 0:
75
94
                name = line[:i]
76
95
                value = line[i+1:]
77
 
                if name.lower() in ('approve', 'approved'):
 
96
                if name.lower() in ('approve',
 
97
                                    'approved',
 
98
                                    'x-approve',
 
99
                                    'x-approved',
 
100
                                    ):
78
101
                    passwd = value.lstrip()
79
102
                    # Now strip the first line from the payload so the
80
103
                    # password doesn't leak.
87
110
            # text part.  We make a pattern from the Approved line and delete
88
111
            # it from all text/* parts in which we find it.  It would be
89
112
            # better to just iterate forward, but email compatability for pre
90
 
            # Python 2.2 returns a list, not a true iterator.
 
113
            # Python 2.2 returns a list, not a true iterator.  Also, there
 
114
            # are pathological MUAs that put the HTML part first.
91
115
            #
92
116
            # This will process all the multipart/alternative parts in the
93
117
            # message as well as all other text parts.  We shouldn't find the
98
122
            # line of HTML or other fancy text may include additional message
99
123
            # text.  This pattern works with HTML.  It may not work with rtf
100
124
            # or whatever else is possible.
101
 
            pattern = name + ':(\s| )*' + re.escape(passwd)
 
125
            #
 
126
            # If we don't find the pattern in the decoded part, but we do
 
127
            # find it after stripping HTML tags, we don't know how to remove
 
128
            # it, so we just reject the post.
 
129
            pattern = name + ':(\xA0|\s| )*' + re.escape(passwd)
102
130
            for part in typed_subpart_iterator(msg, 'text'):
103
131
                if part is not None and part.get_payload() is not None:
104
132
                    lines = part.get_payload(decode=True)
105
133
                    if re.search(pattern, lines):
106
134
                        reset_payload(part, re.sub(pattern, '', lines))
107
 
    if passwd is not missing and mlist.Authenticate((mm_cfg.AuthListModerator,
 
135
                    elif re.search(pattern, re.sub('(?s)<.*?>', '', lines)):
 
136
                        raise Errors.RejectMessage, REJECT
 
137
    if passwd is not missing and mlist.Authenticate((mm_cfg.AuthListPoster,
 
138
                                                     mm_cfg.AuthListModerator,
108
139
                                                     mm_cfg.AuthListAdmin),
109
140
                                                    passwd):
110
141
        # BAW: should we definitely deny if the password exists but does not