Fix #137
[mailman.git] / port_me / checkdbs.py
blob61aa6b6f14253ba213a8020f422bc04fb1437e04
1 # Copyright (C) 1998-2015 by the Free Software Foundation, Inc.
3 # This file is part of GNU Mailman.
5 # GNU Mailman is free software: you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free
7 # Software Foundation, either version 3 of the License, or (at your option)
8 # any later version.
10 # GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 # more details.
15 # You should have received a copy of the GNU General Public License along with
16 # GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
18 import sys
19 import time
20 import optparse
22 from email.Charset import Charset
24 from mailman import MailList
25 from mailman import Utils
26 from mailman.app.requests import handle_request
27 from mailman.configuration import config
28 from mailman.core.i18n import _
29 from mailman.email.message import UserNotification
30 from mailman.initialize import initialize
31 from mailman.interfaces.requests import IListRequests, RequestType
32 from mailman.version import MAILMAN_VERSION
34 # Work around known problems with some RedHat cron daemons
35 import signal
36 signal.signal(signal.SIGCHLD, signal.SIG_DFL)
38 NL = u'\n'
39 now = time.time()
43 def parseargs():
44 parser = optparse.OptionParser(version=MAILMAN_VERSION,
45 usage=_("""\
46 %prog [options]
48 Check for pending admin requests and mail the list owners if necessary."""))
49 parser.add_option('-C', '--config',
50 help=_('Alternative configuration file to use'))
51 opts, args = parser.parse_args()
52 if args:
53 parser.print_help()
54 print(_('Unexpected arguments'), file=sys.stderr)
55 sys.exit(1)
56 return opts, args, parser
60 def pending_requests(mlist):
61 # Must return a byte string
62 lcset = mlist.preferred_language.charset
63 pending = []
64 first = True
65 requestsdb = IListRequests(mlist)
66 for request in requestsdb.of_type(RequestType.subscription):
67 if first:
68 pending.append(_('Pending subscriptions:'))
69 first = False
70 key, data = requestsdb.get_request(request.id)
71 when = data['when']
72 addr = data['addr']
73 fullname = data['fullname']
74 passwd = data['passwd']
75 digest = data['digest']
76 lang = data['lang']
77 if fullname:
78 if isinstance(fullname, unicode):
79 fullname = fullname.encode(lcset, 'replace')
80 fullname = ' (%s)' % fullname
81 pending.append(' %s%s %s' % (addr, fullname, time.ctime(when)))
82 first = True
83 for request in requestsdb.of_type(RequestType.held_message):
84 if first:
85 pending.append(_('\nPending posts:'))
86 first = False
87 key, data = requestsdb.get_request(request.id)
88 when = data['when']
89 sender = data['sender']
90 subject = data['subject']
91 reason = data['reason']
92 text = data['text']
93 msgdata = data['msgdata']
94 subject = Utils.oneline(subject, lcset)
95 date = time.ctime(when)
96 reason = _(reason)
97 pending.append(_("""\
98 From: $sender on $date
99 Subject: $subject
100 Cause: $reason"""))
101 pending.append('')
102 # Coerce all items in pending to a Unicode so we can join them
103 upending = []
104 charset = mlist.preferred_language.charset
105 for s in pending:
106 if isinstance(s, unicode):
107 upending.append(s)
108 else:
109 upending.append(unicode(s, charset, 'replace'))
110 # Make sure that the text we return from here can be encoded to a byte
111 # string in the charset of the list's language. This could fail if for
112 # example, the request was pended while the list's language was French,
113 # but then it was changed to English before checkdbs ran.
114 text = NL.join(upending)
115 charset = Charset(mlist.preferred_language.charset)
116 incodec = charset.input_codec or 'ascii'
117 outcodec = charset.output_codec or 'ascii'
118 if isinstance(text, unicode):
119 return text.encode(outcodec, 'replace')
120 # Be sure this is a byte string encodeable in the list's charset
121 utext = unicode(text, incodec, 'replace')
122 return utext.encode(outcodec, 'replace')
126 def auto_discard(mlist):
127 # Discard old held messages
128 discard_count = 0
129 expire = config.days(mlist.max_days_to_hold)
130 requestsdb = IListRequests(mlist)
131 heldmsgs = list(requestsdb.of_type(RequestType.held_message))
132 if expire and heldmsgs:
133 for request in heldmsgs:
134 key, data = requestsdb.get_request(request.id)
135 if now - data['date'] > expire:
136 handle_request(mlist, request.id, config.DISCARD)
137 discard_count += 1
138 mlist.Save()
139 return discard_count
143 # Figure out epoch seconds of midnight at the start of today (or the given
144 # 3-tuple date of (year, month, day).
145 def midnight(date=None):
146 if date is None:
147 date = time.localtime()[:3]
148 # -1 for dst flag tells the library to figure it out
149 return time.mktime(date + (0,)*5 + (-1,))
152 def main():
153 opts, args, parser = parseargs()
154 initialize(opts.config)
156 for name in config.list_manager.names:
157 # The list must be locked in order to open the requests database
158 mlist = MailList.MailList(name)
159 try:
160 count = IListRequests(mlist).count
161 # While we're at it, let's evict yesterday's autoresponse data
162 midnight_today = midnight()
163 evictions = []
164 for sender in mlist.hold_and_cmd_autoresponses.keys():
165 date, respcount = mlist.hold_and_cmd_autoresponses[sender]
166 if midnight(date) < midnight_today:
167 evictions.append(sender)
168 if evictions:
169 for sender in evictions:
170 del mlist.hold_and_cmd_autoresponses[sender]
171 # This is the only place we've changed the list's database
172 mlist.Save()
173 if count:
174 # Set the default language the the list's preferred language.
175 _.default = mlist.preferred_language
176 realname = mlist.real_name
177 discarded = auto_discard(mlist)
178 if discarded:
179 count = count - discarded
180 text = _('Notice: $discarded old request(s) '
181 'automatically expired.\n\n')
182 else:
183 text = ''
184 if count:
185 text += Utils.maketext(
186 'checkdbs.txt',
187 {'count' : count,
188 'mail_host': mlist.mail_host,
189 'adminDB' : mlist.GetScriptURL('admindb',
190 absolute=1),
191 'real_name': realname,
192 }, mlist=mlist)
193 text += '\n' + pending_requests(mlist)
194 subject = _('$count $realname moderator '
195 'request(s) waiting')
196 else:
197 subject = _('$realname moderator request check result')
198 msg = UserNotification(mlist.GetOwnerEmail(),
199 mlist.GetBouncesEmail(),
200 subject, text,
201 mlist.preferred_language)
202 msg.send(mlist, **{'tomoderators': True})
203 finally:
204 mlist.Unlock()
208 if __name__ == '__main__':
209 main()