def process(self, mlist, msg, msgdata): """See `IHandler`.""" # Short circuit for non-digestable messages. if not mlist.digestable or msgdata.get('isdigest'): return # Open the mailbox that will be used to collect the current digest. mailbox_path = os.path.join(mlist.data_path, 'digest.mmdf') # Lock the mailbox and append the message. with Mailbox(mailbox_path, create=True) as mbox: mbox.add(msg) # Calculate the current size of the mailbox file. This will not tell # us exactly how big the resulting MIME and rfc1153 digest will # actually be, but it's the most easily available metric to decide # whether the size threshold has been reached. size = os.path.getsize(mailbox_path) if size >= mlist.digest_size_threshold * 1024.0: # The digest is ready to send. Because we don't want to hold up # this process with crafting the digest, we're going to move the # digest file to a safe place, then craft a fake message for the # DigestRunner as a trigger for it to build and send the digest. mailbox_dest = os.path.join( mlist.data_path, 'digest.{0.volume}.{0.next_digest_number}.mmdf'.format(mlist)) volume = mlist.volume digest_number = mlist.next_digest_number bump_digest_number_and_volume(mlist) os.rename(mailbox_path, mailbox_dest) config.switchboards['digest'].enqueue(Message(), listid=mlist.list_id, digest_path=mailbox_dest, volume=volume, digest_number=digest_number)
def digest_mbox(mlist): """The mailing list's pending digest as a mailbox. :param mlist: The mailing list. :return: The mailing list's pending digest as a mailbox. """ path = os.path.join(mlist.data_path, 'digest.mmdf') return Mailbox(path)
def process(self, mlist, msg, msgdata): """See `IHandler`.""" # Short-circuit if digests are not enabled or if this message already # is a digest. if not mlist.digests_enabled or msgdata.get('isdigest'): return # Open the mailbox that will be used to collect the current digest. mailbox_path = os.path.join(mlist.data_path, 'digest.mmdf') # Lock the mailbox and append the message. with Mailbox(mailbox_path, create=True) as mbox: mbox.add(msg) maybe_send_digest_now(mlist)
def _dispose(self, mlist, msg, msgdata): """See `IRunner`.""" volume = msgdata['volume'] digest_number = msgdata['digest_number'] # Backslashes make me cry. code = mlist.preferred_language.code with Mailbox(msgdata['digest_path']) as mailbox, _.using(code): # Create the digesters. mime_digest = MIMEDigester(mlist, volume, digest_number) rfc1153_digest = RFC1153Digester(mlist, volume, digest_number) # Cruise through all the messages in the mailbox, first building # the table of contents and accumulating Subject: headers and # authors. The question really is whether it's better from a1 # performance and memory footprint to go through the mailbox once # and cache the messages in a list, or to cruise through the # mailbox twice. We'll do the latter, but it's a complete guess. count = None for count, (key, message) in enumerate(mailbox.iteritems(), 1): mime_digest.add_to_toc(message, count) rfc1153_digest.add_to_toc(message, count) assert count is not None, 'No digest messages?' # Add the table of contents. mime_digest.add_toc(count) rfc1153_digest.add_toc(count) # Cruise through the set of messages a second time, adding them to # the actual digest. for count, (key, message) in enumerate(mailbox.iteritems(), 1): mime_digest.add_message(message, count) rfc1153_digest.add_message(message, count) # Finish up the digests. mime = mime_digest.finish() rfc1153 = rfc1153_digest.finish() # Calculate the recipients lists mime_recipients = set() rfc1153_recipients = set() # When someone turns off digest delivery, they will get one last # digest to ensure that there will be no gaps in the messages they # receive. digest_members = set(mlist.digest_members.members) for member in digest_members: if member.delivery_status is not DeliveryStatus.enabled: continue # Send the digest to the case-preserved address of the digest # members. email_address = member.address.original_email if member.delivery_mode == DeliveryMode.plaintext_digests: rfc1153_recipients.add(email_address) # We currently treat summary_digests the same as mime_digests. elif member.delivery_mode in (DeliveryMode.mime_digests, DeliveryMode.summary_digests): mime_recipients.add(email_address) else: raise AssertionError( 'Digest member "{}" unexpected delivery mode: {}'.format( email_address, member.delivery_mode)) # Add also the folks who are receiving one last digest. for address, delivery_mode in mlist.last_digest_recipients: if delivery_mode == DeliveryMode.plaintext_digests: rfc1153_recipients.add(address.original_email) # We currently treat summary_digests the same as mime_digests. elif delivery_mode in (DeliveryMode.mime_digests, DeliveryMode.summary_digests): mime_recipients.add(address.original_email) else: raise AssertionError( 'OLD recipient "{}" unexpected delivery mode: {}'.format( address, delivery_mode)) # Send the digests to the virgin queue for final delivery. queue = config.switchboards['virgin'] if len(mime_recipients) > 0: queue.enqueue(mime, recipients=mime_recipients, listid=mlist.list_id, isdigest=True) if len(rfc1153_recipients) > 0: queue.enqueue(rfc1153, recipients=rfc1153_recipients, listid=mlist.list_id, isdigest=True) # Remove the digest mbox. (GL #259) os.remove(msgdata['digest_path'])