From a1dec6a0b28d18eb7c682b573259c1a3115bd379 Mon Sep 17 00:00:00 2001 From: Max Erenberg Date: Sat, 10 Apr 2021 01:31:29 -0400 Subject: [PATCH] use maildir for MonthlyArchiver --- .../monthly_archiver.py | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/src/extra_mailman_archivers/monthly_archiver.py b/src/extra_mailman_archivers/monthly_archiver.py index a3156a8..2d5ddc9 100644 --- a/src/extra_mailman_archivers/monthly_archiver.py +++ b/src/extra_mailman_archivers/monthly_archiver.py @@ -20,17 +20,20 @@ # GNU Mailman. If not, see . """ -A simple archiver which copies each message into a folder for the current -month. +A simple archiver which copies each message into a maildir folder for the +current year and month. """ from contextlib import suppress +from datetime import timedelta from email.utils import parsedate -import gzip import logging +from mailbox import Mailbox import os import time +from flufl.lock import Lock +from mailbox import Maildir from mailman.config import config from mailman.interfaces.archiver import IArchiver from public import public @@ -44,7 +47,7 @@ log = logging.getLogger('mailman.archiver') @implementer(IArchiver) class MonthlyArchiver: """ - A simple archiver which copies each message into a folder for the + A simple archiver which copies each message into a maildir for the current month. It is adapted from the `Prototype` archiver. """ @@ -67,8 +70,8 @@ class MonthlyArchiver: def archive_message(mlist, message): """See `IArchiver`. - This archiver saves messages into a folder for the current year and - month, e.g. '2021-April'. + This archiver saves messages into a maildir for the current year and + month, e.g. '2021/April'. :type mlist: mailman.model.listmanager.ListManager :type message: mailman.email.message.Message @@ -81,24 +84,35 @@ class MonthlyArchiver: log.warning('Could not extract date from message %s, using ' 'local time instead' % message_id) date = time.localtime() - year_and_month = time.strftime('%Y-%B', date) + year = time.strftime('%Y') + month = time.strftime('%B') archive_dir = os.path.join( config.ARCHIVE_DIR, MonthlyArchiver.name, mlist.fqdn_listname, - year_and_month + year, + month ) with suppress(FileExistsError): os.makedirs(archive_dir, 0o775) + for subdir in ['cur', 'new', 'tmp']: + with suppress(FileExistsError): + os.makedirs(os.path.join(archive_dir, subdir), 0o775) + maildir = Maildir(archive_dir, create=True) + + # maildir.add() is not thread-safe + lock_file = os.path.join( + config.LOCK_DIR, '{0}-maildir.lock'.format(mlist.fqdn_listname)) + lock = Lock(lock_file) + try: + lock.lock(timeout=timedelta(seconds=3)) + filename = maildir.add(message) + finally: + lock.unlock(unconditionally=True) + # If we fail to acquire the lock, let the error propagate up - # Just use the current timestamp as the file name - filename = str(round(time.time(), 3)) + '.mail.gz' - dst = os.path.join(archive_dir, filename) log.info('MonthlyArchiver archived message %s to %s' % - (message_id, dst)) - # gzip the file to try to save some disk space - with gzip.open(dst, 'wb') as fo: - fo.write(message.as_bytes()) + (message_id, filename)) return None