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
|
# Copyright (C) 2000-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/>.
"""Archive runner."""
__all__ = [
'ArchiveRunner',
]
import copy
import logging
from email.utils import parsedate_tz, mktime_tz
from datetime import datetime
from lazr.config import as_timedelta
from mailman.config import config
from mailman.core.runner import Runner
from mailman.interfaces.archiver import ClobberDate
from mailman.utilities.datetime import RFC822_DATE_FMT, now
from mailman.interfaces.mailinglist import IListArchiverSet
log = logging.getLogger('mailman.error')
def _should_clobber(msg, msgdata, archiver):
"""Should the Date header in the original message get clobbered?"""
# Calculate the Date header of the message as a datetime. What if there
# are multiple Date headers, even in violation of the RFC? For now, take
# the first one. If there are no Date headers, then definitely clobber.
original_date = msg.get('date')
if original_date is None:
return True
section = getattr(config.archiver, archiver, None)
if section is None:
log.error('No archiver config section found: {0}'.format(archiver))
return False
try:
clobber = ClobberDate[section.clobber_date]
except ValueError:
log.error('Invalid clobber_date for "{0}": {1}'.format(
archiver, section.clobber_date))
return False
if clobber is ClobberDate.always:
return True
elif clobber is ClobberDate.never:
return False
# Maybe we'll clobber the date. Let's see if it's farther off from now
# than the skew period.
skew = as_timedelta(section.clobber_skew)
try:
time_tuple = parsedate_tz(original_date)
except (ValueError, OverflowError):
# The likely cause of this is that the year in the Date: field is
# horribly incorrect, e.g. (from SF bug # 571634):
#
# Date: Tue, 18 Jun 0102 05:12:09 +0500
#
# Obviously clobber such dates.
return True
if time_tuple is None:
# There was some other bogosity in the Date header.
return True
claimed_date = datetime.fromtimestamp(mktime_tz(time_tuple))
return (abs(now() - claimed_date) > skew)
class ArchiveRunner(Runner):
"""The archive runner."""
def _dispose(self, mlist, msg, msgdata):
received_time = msgdata.get('received_time', now(strip_tzinfo=False))
archiver_set = IListArchiverSet(mlist)
for archiver in archiver_set.archivers:
# The archiver is disabled if either the list-specific or
# site-wide archiver is disabled.
if not archiver.is_enabled:
continue
msg_copy = copy.deepcopy(msg)
if _should_clobber(msg, msgdata, archiver.name):
original_date = msg_copy['date']
del msg_copy['date']
del msg_copy['x-original-date']
msg_copy['Date'] = received_time.strftime(RFC822_DATE_FMT)
if original_date:
msg_copy['X-Original-Date'] = original_date
# A problem in one archiver should not prevent other archivers
# from running.
try:
archiver.system_archiver.archive_message(mlist, msg_copy)
except Exception:
log.exception('Broken archiver: %s' % archiver.name)
|