119 lines
3.7 KiB
Python
119 lines
3.7 KiB
Python
# 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
|