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
|
# Copyright (C) 1998-2012 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/>.
"""Handler for automatic responses."""
from __future__ import absolute_import, unicode_literals
__metaclass__ = type
__all__ = [
'Replybot',
]
import logging
from zope.component import getUtility
from zope.interface import implements
from mailman.core.i18n import _
from mailman.email.message import UserNotification
from mailman.interfaces.autorespond import (
ALWAYS_REPLY, IAutoResponseSet, Response, ResponseAction)
from mailman.interfaces.handler import IHandler
from mailman.interfaces.usermanager import IUserManager
from mailman.utilities.datetime import today
from mailman.utilities.string import expand, wrap
log = logging.getLogger('mailman.error')
class Replybot:
"""Send automatic responses."""
implements(IHandler)
name = 'replybot'
description = _('Send automatic responses.')
def process(self, mlist, msg, msgdata):
"""See `IHandler`."""
# There are several cases where the replybot is short-circuited:
# * the original message has an "X-Ack: No" header
# * the message has a Precedence header with values bulk, junk, or
# list, and there's no explicit "X-Ack: yes" header
# * the message metadata has a true 'noack' key
ack = msg.get('x-ack', '').lower()
if ack == 'no' or msgdata.get('noack'):
return
precedence = msg.get('precedence', '').lower()
if ack != 'yes' and precedence in ('bulk', 'junk', 'list'):
return
# Check to see if the list is even configured to autorespond to this
# email message. Note: the incoming message processors should set the
# destination key in the message data.
if msgdata.get('to_owner'):
if mlist.autorespond_owner is ResponseAction.none:
return
response_type = Response.owner
response_text = mlist.autoresponse_owner_text
elif msgdata.get('to_request'):
if mlist.autorespond_requests is ResponseAction.none:
return
response_type = Response.command
response_text = mlist.autoresponse_request_text
elif msgdata.get('to_list'):
if mlist.autorespond_postings is ResponseAction.none:
return
response_type = Response.postings
response_text = mlist.autoresponse_postings_text
else:
# There are no automatic responses for any other destination.
return
# Now see if we're in the grace period for this sender. grace_period
# = 0 means always automatically respond, as does an "X-Ack: yes"
# header (useful for debugging).
response_set = IAutoResponseSet(mlist)
user_manager = getUtility(IUserManager)
address = user_manager.get_address(msg.sender)
if address is None:
address = user_manager.create_address(msg.sender)
grace_period = mlist.autoresponse_grace_period
if grace_period > ALWAYS_REPLY and ack != 'yes':
last = response_set.last_response(address, response_type)
if last is not None and last.date_sent + grace_period > today():
return
# Okay, we know we're going to respond to this sender, craft the
# message, send it, and update the database.
display_name = mlist.display_name
subject = _(
'Auto-response for your message to the "$display_name" '
'mailing list')
# Do string interpolation into the autoresponse text
d = dict(list_name = mlist.list_name,
display_name = display_name,
listurl = mlist.script_url('listinfo'),
requestemail = mlist.request_address,
owneremail = mlist.owner_address,
)
# Interpolation and Wrap the response text.
text = wrap(expand(response_text, d))
outmsg = UserNotification(msg.sender, mlist.bounces_address,
subject, text, mlist.preferred_language)
outmsg['X-Mailer'] = _('The Mailman Replybot')
# prevent recursions and mail loops!
outmsg['X-Ack'] = 'No'
outmsg.send(mlist)
response_set.response_sent(address, response_type)
|