first commit

Max Erenberg 2 years ago
commit e824785e0a
  1. 32
  2. 23
  3. 3
  4. 0
  5. 108

@ -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:
\_ monthly_archiver
\_ 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:
apt install python3-pip python3-setuptools python3-wheel
pip3 install .

@ -0,0 +1,23 @@
name = extra-mailman-archivers
version = 0.0.1
author = Max Erenberg
author_email =
description = Some extra archivers for Mailman 3
long_description = file:
long_description_content_type = text/markdown
url =
license = GPLv3
classifiers =
Programming Language :: Python :: 3
OSI Approved :: GNU General Public License v3 or later (GPLv3+)
Operating System :: OS Independent
package_dir =
= src
packages = find:
python_requires = >=3.5
where = src

@ -0,0 +1,3 @@
import setuptools

@ -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 <>.
A simple archiver which copies each message into a folder for the current
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')
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
def list_url(mlist):
"""See `IArchiver`."""
# This archiver is not web-accessible, therefore no URL is returned.
return None
def permalink(mlist, msg):
"""See `IArchiver`."""
# This archiver is not web-accessible, therefore no URL is returned.
return None
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:
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))
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(
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)'MonthlyArchiver archived message %s to %s' %
(message_id, dst))
# gzip the file to try to save some disk space
with, 'wb') as fo:
return None