#! @PYTHON@
#
# Copyright (C) 1998-2018 by the Free Software Foundation, Inc.
#
# This program 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 2
# of the License, or (at your option) any later version.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.
"""Check for pending admin requests and mail the list owners if necessary.
Usage: %(PROGRAM)s [options]
Options:
-h/--help
Print this message and exit.
"""
import sys
import time
import getopt
from types import UnicodeType
import paths
# Import this after paths so we get Mailman's copy of the email package
from email.Charset import Charset
from Mailman import mm_cfg
from Mailman import Utils
from Mailman import MailList
from Mailman import Message
from Mailman import i18n
# Work around known problems with some RedHat cron daemons
import signal
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
NL = '\n'
PROGRAM = sys.argv[0]
_ = i18n._
i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
now = time.time()
def usage(code, msg=''):
if code:
fd = sys.stderr
else:
fd = sys.stdout
print >> fd, _(__doc__)
if msg:
print >> fd, msg
sys.exit(code)
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], 'h', ['help'])
except getopt.error, msg:
usage(1, msg)
for opt, arg in opts:
if opt in ('-h', '--help'):
usage(0)
if args:
usage(1)
for name in Utils.list_names():
# the list must be locked in order to open the requests database
mlist = MailList.MailList(name)
try:
count = mlist.NumRequestsPending()
# While we're at it, let's evict yesterday's autoresponse data
midnightToday = Utils.midnight()
evictions = []
for sender in mlist.hold_and_cmd_autoresponses.keys():
date, respcount = mlist.hold_and_cmd_autoresponses[sender]
if Utils.midnight(date) < midnightToday:
evictions.append(sender)
if evictions:
for sender in evictions:
del mlist.hold_and_cmd_autoresponses[sender]
# Only here have we changed the list's database
mlist.Save()
if count:
i18n.set_language(mlist.preferred_language)
realname = mlist.real_name
discarded = auto_discard(mlist)
if discarded:
count = count - discarded
text = _(
'Notice: %(discarded)d old request(s) automatically expired.\n\n')
else:
text = ''
if count:
text += Utils.maketext(
'checkdbs.txt',
{'count' : count,
'host_name': mlist.host_name,
'adminDB' : mlist.GetScriptURL('admindb', absolute=1),
'real_name': realname,
}, mlist=mlist)
text += '\n' + pending_requests(mlist)
subject = _(
'%(count)d %(realname)s moderator request(s) waiting')
else:
subject = _(
'%(realname)s moderator request check result')
msg = Message.UserNotification(mlist.GetOwnerEmail(),
mlist.GetBouncesEmail(),
subject, text,
mlist.preferred_language)
msg.send(mlist, **{'tomoderators': 1})
finally:
mlist.Unlock()
def pending_requests(mlist):
# Must return a byte string
lcset = Utils.GetCharSet(mlist.preferred_language)
pending = []
first = 1
for id in mlist.GetSubscriptionIds():
if first:
pending.append(_('Pending subscriptions:'))
first = 0
when, addr, fullname, passwd, digest, lang = mlist.GetRecord(id)
if fullname:
if isinstance(fullname, UnicodeType):
fullname = fullname.encode(lcset, 'replace')
fullname = ' (%s)' % fullname
pending.append(' %s%s %s' % (addr, fullname, time.ctime(when)))
first = 1
for id in mlist.GetUnsubscriptionIds():
if first:
pending.append(_('Pending unsubscriptions:'))
first = 0
addr = mlist.GetRecord(id)
pending.append(' %s' % addr)
first = 1
for id in mlist.GetHeldMessageIds():
if first:
pending.append(_('\nPending posts:'))
first = 0
info = mlist.GetRecord(id)
when, sender, subject, reason, text, msgdata = mlist.GetRecord(id)
subject = Utils.oneline(subject, lcset)
date = time.ctime(when)
reason = _(reason)
pending.append(_("""\
From: %(sender)s on %(date)s
Subject: %(subject)s
Cause: %(reason)s"""))
pending.append('')
# Coerce all items in pending to a Unicode so we can join them
upending = []
charset = Utils.GetCharSet(mlist.preferred_language)
for s in pending:
if isinstance(s, UnicodeType):
upending.append(s)
else:
upending.append(unicode(s, charset, 'replace'))
# Make sure that the text we return from here can be encoded to a byte
# string in the charset of the list's language. This could fail if for
# example, the request was pended while the list's language was French,
# but then it was changed to English before checkdbs ran.
text = u'\n'.join(upending)
charset = Charset(Utils.GetCharSet(mlist.preferred_language))
incodec = charset.input_codec or 'ascii'
outcodec = charset.output_codec or 'ascii'
if isinstance(text, UnicodeType):
return text.encode(outcodec, 'replace')
# Be sure this is a byte string encodeable in the list's charset
utext = unicode(text, incodec, 'replace')
return utext.encode(outcodec, 'replace')
def auto_discard(mlist):
# Discard old held messages
discard_count = 0
expire = mlist.max_days_to_hold * 86400 # days
heldmsgs = mlist.GetHeldMessageIds()
if expire and len(heldmsgs):
for id in heldmsgs:
if now - mlist.GetRecord(id)[0] > expire:
mlist.HandleRequest(id, mm_cfg.DISCARD)
discard_count += 1
mlist.Save()
return discard_count
if __name__ == '__main__':
main()