1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
# Copyright (C) 2011-2015 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
# GNU Mailman is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
"""RFC 2369 List-* and related headers."""
__all__ = [
'RFC2369',
]
from email.utils import formataddr
from mailman.core.i18n import _
from mailman.handlers.cook_headers import uheader
from mailman.interfaces.archiver import ArchivePolicy
from mailman.interfaces.mailinglist import IListArchiverSet
from mailman.interfaces.handler import IHandler
from zope.interface import implementer
CONTINUATION = ',\n\t'
def process(mlist, msg, msgdata):
"""Add the RFC 2369 List-* and related headers."""
# Some people really hate the List-* headers. It seems that the free
# version of Eudora (possibly on for some platforms) does not hide these
# headers by default, pissing off their users. Too bad. Fix the MUAs.
if not mlist.include_rfc2369_headers:
return
list_id = '{0.list_name}.{0.mail_host}'.format(mlist)
if mlist.description:
# Don't wrap the header since here we just want to get it properly RFC
# 2047 encoded.
i18ndesc = uheader(mlist, mlist.description, 'List-Id', maxlinelen=998)
listid_h = formataddr((str(i18ndesc), list_id))
else:
# Without a description, we need to ensure the MUST brackets.
listid_h = '<{}>'.format(list_id)
# No other agent should add a List-ID header except Mailman.
del msg['list-id']
msg['List-Id'] = listid_h
# For internally crafted messages, we also add a (nonstandard),
# "X-List-Administrivia: yes" header. For all others (i.e. those coming
# from list posts), we add a bunch of other RFC 2369 headers.
requestaddr = mlist.request_address
subfieldfmt = '<{}>, <mailto:{}>'
listinfo = mlist.script_url('listinfo')
headers = []
# XXX reduced_list_headers used to suppress List-Help, List-Subject, and
# List-Unsubscribe from UserNotification. That doesn't seem to make sense
# any more, so always add those three headers (others will still be
# suppressed).
headers.extend((
('List-Help', '<mailto:{}?subject=help>'.format(requestaddr)),
('List-Unsubscribe',
subfieldfmt.format(listinfo, mlist.leave_address)),
('List-Subscribe', subfieldfmt.format(listinfo, mlist.join_address)),
))
if not msgdata.get('reduced_list_headers'):
# List-Post: is controlled by a separate attribute, which is somewhat
# misnamed. RFC 2369 requires a value of NO if posting is not
# allowed, i.e. for an announce-only list.
list_post = ('<mailto:{}>'.format(mlist.posting_address)
if mlist.allow_list_posts
else 'NO')
headers.append(('List-Post', list_post))
# Add RFC 2369 and 5064 archiving headers, if archiving is enabled.
if mlist.archive_policy is not ArchivePolicy.never:
archiver_set = IListArchiverSet(mlist)
for archiver in archiver_set.archivers:
if not archiver.is_enabled:
continue
archiver_url = archiver.system_archiver.list_url(mlist)
if archiver_url is not None:
headers.append(('List-Archive',
'<{}>'.format(archiver_url)))
permalink = archiver.system_archiver.permalink(mlist, msg)
if permalink is not None:
headers.append(('Archived-At', '<{}>'.format(permalink)))
# XXX RFC 2369 also defines a List-Owner header which we are not currently
# supporting, but should.
#
# Some headers will appear more than once in the new set, e.g. the
# List-Archive and Archived-At headers. We want to delete any RFC 2369
# headers from the original message, but make sure to preserve all of the
# new headers we're adding. Go through the list of new headers twice,
# first removing any old ones, then adding all the new ones.
for h, v in headers:
del msg[h]
for h, v in sorted(headers):
# Wrap these lines if they are too long. 78 character width probably
# shouldn't be hardcoded, but is at least text-MUA friendly. The
# adding of 2 is for the colon-space separator.
if len(h) + 2 + len(v) > 78:
v = CONTINUATION.join(v.split(', '))
msg[h] = v
@implementer(IHandler)
class RFC2369:
"""Add the RFC 2369 List-* headers."""
name = 'rfc-2369'
description = _('Add the RFC 2369 List-* headers.')
def process(self, mlist, msg, msgdata):
"""See `IHandler`."""
process(mlist, msg, msgdata)
|