64
64
charset = 'us-ascii'
65
65
return Header(s, charset, maxlinelen, header_name, continuation_ws)
67
def change_header(name, value, mlist, msg, msgdata, delete=True, repl=True):
68
if mm_cfg.ALLOW_AUTHOR_IS_LIST and mlist.author_is_list == 2:
69
msgdata.setdefault('add_header', {})[name] = value
70
elif repl or not msg.has_key(name):
69
77
def process(mlist, msg, msgdata):
70
78
# Set the "X-Ack: no" header if noack flag is set.
71
79
if msgdata.get('noack'):
80
change_header('X-Ack', 'no', mlist, msg, msgdata)
74
81
# Because we're going to modify various important headers in the email
75
82
# message, we want to save some of the information in the msgdata
76
83
# dictionary for later. Specifically, the sender header will get waxed,
88
95
# Mark message so we know we've been here, but leave any existing
89
96
# X-BeenThere's intact.
90
msg['X-BeenThere'] = mlist.GetListEmail()
97
change_header('X-BeenThere', mlist.GetListEmail(),
98
mlist, msg, msgdata, delete=False)
91
99
# Add Precedence: and other useful headers. None of these are standard
92
100
# and finding information on some of them are fairly difficult. Some are
93
101
# just common practice, and we'll add more here as they become necessary.
101
109
# known exploits in a particular version of Mailman and we know a site is
102
110
# using such an old version, they may be vulnerable. It's too easy to
103
111
# edit the code to add a configuration variable to handle this.
104
if not msg.has_key('x-mailman-version'):
105
msg['X-Mailman-Version'] = mm_cfg.VERSION
112
change_header('X-Mailman-Version', mm_cfg.VERSION,
113
mlist, msg, msgdata, repl=False)
106
114
# We set "Precedence: list" because this is the recommendation from the
107
115
# sendmail docs, the most authoritative source of this header's semantics.
108
if not msg.has_key('precedence'):
109
msg['Precedence'] = 'list'
116
change_header('Precedence', 'list',
117
mlist, msg, msgdata, repl=False)
118
# Do we change the from so the list takes ownership of the email
119
if mm_cfg.ALLOW_AUTHOR_IS_LIST and mlist.author_is_list:
120
realname, email = parseaddr(msg['from'])
121
replies = getaddresses(msg.get('reply-to', ''))
122
reply_addrs = [x[1].lower() for x in replies]
124
if email.lower() not in reply_addrs:
125
rt = msg['reply-to'] + ', ' + msg['from']
130
change_header('Reply-To', rt, mlist, msg, msgdata)
131
change_header('From',
132
formataddr(('%s via %s' % (realname, mlist.real_name),
133
mlist.GetListEmail())),
135
if mlist.author_is_list != 2:
137
#MAS ?? mlist.include_sender_header = 0
110
138
# Reply-To: munging. Do not do this if the message is "fast tracked",
111
139
# meaning it is internally crafted and delivered to a specific user. BAW:
112
140
# Yuck, I really hate this feature but I've caved under the sheer pressure
142
170
if mlist.reply_goes_to_list == 1:
143
171
i18ndesc = uheader(mlist, mlist.description, 'Reply-To')
144
172
add((str(i18ndesc), mlist.GetListEmail()))
146
173
# Don't put Reply-To: back if there's nothing to add!
149
msg['Reply-To'] = COMMASPACE.join(
150
[formataddr(pair) for pair in new])
176
change_header('Reply-To',
177
COMMASPACE.join([formataddr(pair) for pair in new]),
151
181
# The To field normally contains the list posting address. However
152
182
# when messages are fully personalized, that header will get
153
183
# overwritten with the address of the recipient. We need to get the
171
201
i18ndesc = uheader(mlist, mlist.description, 'Cc')
172
202
add((str(i18ndesc), mlist.GetListEmail()))
203
# We don't worry about what AvoidDuplicates may have done with a
204
# Cc: header or using change_header here since we never get here
205
# if author_is_list is allowed and True.
174
207
msg['Cc'] = COMMASPACE.join([formataddr(pair) for pair in new])
175
208
# Add list-specific headers as defined in RFC 2369 and RFC 2919, but only
193
226
# without desc we need to ensure the MUST brackets
194
227
listid_h = '<%s>' % listid
195
228
# We always add a List-ID: header.
197
msg['List-Id'] = listid_h
229
change_header('List-Id', listid_h, mlist, msg, msgdata)
198
230
# For internally crafted messages, we also add a (nonstandard),
199
231
# "X-List-Administrivia: yes" header. For all others (i.e. those coming
200
232
# from list posts), we add a bunch of other RFC 2369 headers.
221
253
# First we delete any pre-existing headers because the RFC permits only
222
254
# one copy of each, and we want to be sure it's ours.
223
255
for h, v in headers.items():
225
256
# Wrap these lines if they are too long. 78 character width probably
226
257
# shouldn't be hardcoded, but is at least text-MUA friendly. The
227
258
# adding of 2 is for the colon-space separator.
228
259
if len(h) + 2 + len(v) > 78:
229
260
v = CONTINUATION.join(v.split(', '))
261
change_header(h, v, mlist, msg, msgdata)
304
335
h = u' '.join([prefix, subject])
305
336
h = h.encode('us-ascii')
306
337
h = uheader(mlist, h, 'Subject', continuation_ws=ws)
338
change_header('Subject', h, mlist, msg, msgdata)
309
339
ss = u' '.join([recolon, subject])
310
340
ss = ss.encode('us-ascii')
311
341
ss = uheader(mlist, ss, 'Subject', continuation_ws=ws)
323
353
# TK: Subject is concatenated and unicode string.
324
354
subject = subject.encode(cset, 'replace')
325
355
h.append(subject, cset)
356
change_header('Subject', h, mlist, msg, msgdata)
328
357
ss = uheader(mlist, recolon, 'Subject', continuation_ws=ws)
329
358
ss.append(subject, cset)
330
359
msgdata['stripped_subject'] = ss