first commit
This commit is contained in:
commit
e824785e0a
|
@ -0,0 +1,32 @@
|
|||
# Extra Mailman Archivers
|
||||
|
||||
This contains some extra archivers to be used with Mailman 3.
|
||||
Currently there is only one archiver, `MonthlyArchiver`, which simply gzips
|
||||
each message and stores it in a folder named by the year and month. Each
|
||||
message's file name is a Unix epoch timestamp.
|
||||
|
||||
Example directory structure:
|
||||
```
|
||||
/var/lib/mailman3/archives
|
||||
\_ monthly_archiver
|
||||
\_ mylist@mydomain.com
|
||||
\_ 2021-April
|
||||
\_ 1617678398.173.mail.gz
|
||||
\_ 1617678805.351.mail.gz
|
||||
\_ 2021-May
|
||||
\_ ...
|
||||
```
|
||||
|
||||
The motivation behind `MonthlyArchiver` was to avoid storing thousands of
|
||||
messages in a single directory, which the `Prototype` archiver currently
|
||||
does since it uses a maildir.
|
||||
|
||||
## Installation
|
||||
Make sure to have the following packages installed first:
|
||||
```sh
|
||||
apt install python3-pip python3-setuptools python3-wheel
|
||||
```
|
||||
Then:
|
||||
```sh
|
||||
pip3 install .
|
||||
```
|
|
@ -0,0 +1,23 @@
|
|||
[metadata]
|
||||
name = extra-mailman-archivers
|
||||
version = 0.0.1
|
||||
author = Max Erenberg
|
||||
author_email = merenber@csclub.uwaterloo.ca
|
||||
description = Some extra archivers for Mailman 3
|
||||
long_description = file: README.md
|
||||
long_description_content_type = text/markdown
|
||||
url = https://git.csclub.uwaterloo.ca/merenber/extra-mailman-archivers.git
|
||||
license = GPLv3
|
||||
classifiers =
|
||||
Programming Language :: Python :: 3
|
||||
OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
||||
Operating System :: OS Independent
|
||||
|
||||
[options]
|
||||
package_dir =
|
||||
= src
|
||||
packages = find:
|
||||
python_requires = >=3.5
|
||||
|
||||
[options.packages.find]
|
||||
where = src
|
|
@ -0,0 +1,108 @@
|
|||
# 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 folder for the current
|
||||
month.
|
||||
"""
|
||||
|
||||
from contextlib import suppress
|
||||
import datetime
|
||||
from datetime import timedelta
|
||||
from email.utils import parsedate
|
||||
import gzip
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
|
||||
from flufl.lock import Lock, TimeOutError
|
||||
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.error')
|
||||
|
||||
|
||||
@public
|
||||
@implementer(IArchiver)
|
||||
class MonthlyArchiver:
|
||||
"""
|
||||
A simple archiver which copies each message into a folder 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 folder for the current year and
|
||||
month, e.g. '2021-April'.
|
||||
|
||||
:type mlist: mailman.model.listmanager.ListManager
|
||||
: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_and_month = time.strftime('%Y-%B', date)
|
||||
|
||||
archive_dir = os.path.join(
|
||||
config.ARCHIVE_DIR,
|
||||
MonthlyArchiver.name,
|
||||
mlist.fqdn_listname,
|
||||
year_and_month
|
||||
)
|
||||
with suppress(FileExistsError):
|
||||
os.makedirs(archive_dir, 0o775)
|
||||
|
||||
# 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())
|
||||
|
||||
return None
|
Loading…
Reference in New Issue