Extra archivers for Mailman 3.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

118 lines
3.7 KiB

# Copyright (C) 2021 Max Erenberg
# This file is adapted from GNU Mailman. The original copyright notice
# is preserved below.
#
# Copyright (C) 2008-2019 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
# GNU Mailman 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 3 of the License, or (at your option)
# any later version.
#
# GNU Mailman 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
# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
"""
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 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
from zope.interface import implementer
log = logging.getLogger('mailman.archiver')
@public
@implementer(IArchiver)
class MonthlyArchiver:
"""
A simple archiver which copies each message into a maildir for the
current month. It is adapted from the `Prototype` archiver.
"""
name = 'monthly_archiver'
is_enabled = False
@staticmethod
def list_url(mlist):
"""See `IArchiver`."""
# This archiver is not web-accessible, therefore no URL is returned.
return None
@staticmethod
def permalink(mlist, msg):
"""See `IArchiver`."""
# This archiver is not web-accessible, therefore no URL is returned.
return None
@staticmethod
def archive_message(mlist, message):
"""See `IArchiver`.
This archiver saves messages into a maildir for the current year and
month, e.g. '2021/April'.
:type mlist: mailman.interfaces.mailinglist.IMailingList
:type message: mailman.email.message.Message
"""
message_id = message.get('message-id')
date_secs = parsedate(message.get('date'))
if date_secs is not None:
date = time.localtime(time.mktime(date_secs))
else:
log.warning('Could not extract date from message %s, using '
'local time instead' % message_id)
date = time.localtime()
year = time.strftime('%Y')
month = time.strftime('%B')
archive_dir = os.path.join(
config.ARCHIVE_DIR,
MonthlyArchiver.name,
mlist.fqdn_listname,
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
log.info('MonthlyArchiver archived message %s to %s' %
(message_id, filename))
return None