"""Add the message to the list's current digest and possibly send it."""
import os
import re
import copy
import time
from types import ListType
from cStringIO import StringIO
from email.Parser import Parser
from email.Generator import Generator
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.MIMEMessage import MIMEMessage
from email.Utils import getaddresses, formatdate
from email.Header import decode_header, make_header, Header
from email.Charset import Charset
from Mailman import mm_cfg
from Mailman import Utils
from Mailman import Message
from Mailman import i18n
from Mailman import Errors
from Mailman.Mailbox import Mailbox
from Mailman.MemberAdaptor import ENABLED
from Mailman.Handlers.Decorate import decorate
from Mailman.Queue.sbcache import get_switchboard
from Mailman.Mailbox import Mailbox
from Mailman.Handlers.Scrubber import process as scrubber
from Mailman.Logging.Syslog import syslog
_ = i18n._
UEMPTYSTRING = u''
EMPTYSTRING = ''
try:
True, False
except NameError:
True = 1
False = 0
def process(mlist, msg, msgdata):
if not mlist.digestable or msgdata.get('isdigest'):
return
mboxfile = os.path.join(mlist.fullpath(), 'digest.mbox')
omask = os.umask(007)
try:
mboxfp = open(mboxfile, 'a+')
finally:
os.umask(omask)
mbox = Mailbox(mboxfp)
mbox.AppendMessage(msg)
mboxfp.flush()
size = os.path.getsize(mboxfile)
if size / 1024.0 >= mlist.digest_size_threshhold:
try:
mboxfp.seek(0)
send_digests(mlist, mboxfp)
os.unlink(mboxfile)
except Exception, errmsg:
syslog('error', 'send_digests() failed: %s', errmsg)
mboxfp.close()
def send_digests(mlist, mboxfp):
if mlist.digest_last_sent_at:
bump = False
timetup = time.localtime(mlist.digest_last_sent_at)
now = time.localtime(time.time())
freq = mlist.digest_volume_frequency
if freq == 0 and timetup[0] < now[0]:
bump = True
elif freq == 1 and timetup[1] <> now[1]:
bump = True
elif freq == 2 and (timetup[1] % 4 <> now[1] % 4):
bump = True
elif freq == 3:
weeknum_last = int(time.strftime('%W', timetup))
weeknum_now = int(time.strftime('%W', now))
if weeknum_now > weeknum_last or timetup[0] > now[0]:
bump = True
elif freq == 4 and timetup[7] <> now[7]:
bump = True
if bump:
mlist.bump_digest_volume()
mlist.digest_last_sent_at = time.time()
otranslation = i18n.get_translation()
i18n.set_language(mlist.preferred_language)
try:
send_i18n_digests(mlist, mboxfp)
finally:
i18n.set_translation(otranslation)
def send_i18n_digests(mlist, mboxfp):
mbox = Mailbox(mboxfp)
lang = mlist.preferred_language
lcset = Utils.GetCharSet(lang)
lcset_out = Charset(lcset).output_charset or lcset
realname = mlist.real_name
volume = mlist.volume
issue = mlist.next_digest_number
digestid = _('%(realname)s Digest, Vol %(volume)d, Issue %(issue)d')
digestsubj = Header(digestid, lcset, header_name='Subject')
mimemsg = Message.Message()
mimemsg['Content-Type'] = 'multipart/mixed'
mimemsg['MIME-Version'] = '1.0'
mimemsg['From'] = mlist.GetRequestEmail()
mimemsg['Subject'] = digestsubj
mimemsg['To'] = mlist.GetListEmail()
mimemsg['Reply-To'] = mlist.GetListEmail()
mimemsg['Date'] = formatdate(localtime=1)
mimemsg['Message-ID'] = Utils.unique_message_id(mlist)
plainmsg = StringIO()
rfc1153msg = Message.Message()
rfc1153msg['From'] = mlist.GetRequestEmail()
rfc1153msg['Subject'] = digestsubj
rfc1153msg['To'] = mlist.GetListEmail()
rfc1153msg['Reply-To'] = mlist.GetListEmail()
rfc1153msg['Date'] = formatdate(localtime=1)
rfc1153msg['Message-ID'] = Utils.unique_message_id(mlist)
separator70 = '-' * 70
separator30 = '-' * 30
mastheadtxt = Utils.maketext(
'masthead.txt',
{'real_name' : mlist.real_name,
'got_list_email': mlist.GetListEmail(),
'got_listinfo_url': mlist.GetScriptURL('listinfo', absolute=1),
'got_request_email': mlist.GetRequestEmail(),
'got_owner_email': mlist.GetOwnerEmail(),
}, mlist=mlist)
masthead = MIMEText(mastheadtxt, _charset=lcset)
masthead['Content-Description'] = digestid
mimemsg.attach(masthead)
print >> plainmsg, mastheadtxt
print >> plainmsg
if mlist.digest_header:
headertxt = decorate(mlist, mlist.digest_header, _('digest header'))
header = MIMEText(headertxt, _charset=lcset)
header['Content-Description'] = _('Digest Header')
mimemsg.attach(header)
print >> plainmsg, headertxt
print >> plainmsg
toc = StringIO()
print >> toc, _("Today's Topics:\n")
messages = []
msgcount = 0
msg = mbox.next()
while msg is not None:
if msg == '':
msg = mbox.next()
continue
msgcount += 1
messages.append(msg)
msgsubj = msg.get('subject', _('(no subject)'))
subject = Utils.oneline(msgsubj, lcset)
mo = re.match('(re:? *)?(%s)' % re.escape(mlist.subject_prefix),
subject, re.IGNORECASE)
if mo:
subject = subject[:mo.start(2)] + subject[mo.end(2):]
username = ''
addresses = getaddresses([Utils.oneline(msg.get('from', ''), lcset)])
if isinstance(addresses, ListType) and addresses:
username = addresses[0][0]
if not username:
username = addresses[0][1]
if username:
username = ' (%s)' % username
wrapped = Utils.wrap('%2d. %s' % (msgcount, subject), 65)
slines = wrapped.split('\n')
if len(slines[-1]) + len(username) > 70:
slines.append(username)
else:
slines[-1] += username
first = True
for line in slines:
if first:
print >> toc, ' ', line
first = False
else:
print >> toc, ' ', line.lstrip()
keeper = {}
all_keepers = {}
for header in (mm_cfg.MIME_DIGEST_KEEP_HEADERS +
mm_cfg.PLAIN_DIGEST_KEEP_HEADERS):
all_keepers[header] = True
all_keepers = all_keepers.keys()
for keep in all_keepers:
keeper[keep] = msg.get_all(keep, [])
for header in msg.keys():
del msg[header]
for keep in all_keepers:
for field in keeper[keep]:
msg[keep] = field
msg['Message'] = `msgcount`
msg = mbox.next()
if msgcount == 0:
return
toctext = toc.getvalue()
tocpart = MIMEText(toctext, _charset=lcset)
tocpart['Content-Description']= _("Today's Topics (%(msgcount)d messages)")
mimemsg.attach(tocpart)
print >> plainmsg, toctext
print >> plainmsg
print >> plainmsg, separator70
print >> plainmsg
mimedigest = MIMEBase('multipart', 'digest')
mimemsg.attach(mimedigest)
first = True
for msg in messages:
mimedigest.attach(MIMEMessage(copy.deepcopy(msg)))
if first:
first = False
else:
print >> plainmsg, separator30
print >> plainmsg
try:
msg = scrubber(mlist, msg)
except Errors.DiscardMessage:
print >> plainmsg, _('[Message discarded by content filter]')
continue
for h in mm_cfg.PLAIN_DIGEST_KEEP_HEADERS:
if msg[h]:
uh = Utils.wrap('%s: %s' % (h, Utils.oneline(msg[h], lcset)))
uh = '\n\t'.join(uh.split('\n'))
print >> plainmsg, uh
print >> plainmsg
payload = msg.get_payload(decode=True) \
or msg.as_string().split('\n\n',1)[1]
mcset = msg.get_content_charset('')
if mcset and mcset <> lcset and mcset <> lcset_out:
try:
payload = unicode(payload, mcset, 'replace'
).encode(lcset, 'replace')
except (UnicodeError, LookupError):
payload = unicode(payload, lcset_out, 'replace'
).encode(lcset, 'replace')
print >> plainmsg, payload
if not payload.endswith('\n'):
print >> plainmsg
if mlist.digest_footer:
footertxt = decorate(mlist, mlist.digest_footer, _('digest footer'))
footer = MIMEText(footertxt, _charset=lcset)
footer['Content-Description'] = _('Digest Footer')
mimemsg.attach(footer)
print >> plainmsg, separator30
print >> plainmsg
print >> plainmsg, footertxt
print >> plainmsg
signoff = _('End of ') + digestid
mimemsg.postamble = signoff
print >> plainmsg, signoff
print >> plainmsg, '*' * len(signoff)
mlist.next_digest_number += 1
virginq = get_switchboard(mm_cfg.VIRGINQUEUE_DIR)
plainrecips = []
mimerecips = []
drecips = mlist.getDigestMemberKeys() + mlist.one_last_digest.keys()
for user in mlist.getMemberCPAddresses(drecips):
if user is None or mlist.getDeliveryStatus(user) <> ENABLED:
continue
if mlist.getMemberOption(user, mm_cfg.DisableMime):
plainrecips.append(user)
else:
mimerecips.append(user)
mlist.one_last_digest.clear()
virginq.enqueue(mimemsg,
recips=mimerecips,
listname=mlist.internal_name(),
isdigest=True)
rfc1153msg.set_payload(plainmsg.getvalue(), lcset)
virginq.enqueue(rfc1153msg,
recips=plainrecips,
listname=mlist.internal_name(),
isdigest=True)