diff options
| author | Barry Warsaw | 2008-07-05 11:06:37 -0400 |
|---|---|---|
| committer | Barry Warsaw | 2008-07-05 11:06:37 -0400 |
| commit | 08400a46afb740a0e49058707969462fa7e7dddf (patch) | |
| tree | 42d18be4b2bda21680e88d5a0b19495282d4aa19 /mailman | |
| parent | dda21fc619518344dfc44081c64674f6374fe404 (diff) | |
| download | mailman-08400a46afb740a0e49058707969462fa7e7dddf.tar.gz mailman-08400a46afb740a0e49058707969462fa7e7dddf.tar.zst mailman-08400a46afb740a0e49058707969462fa7e7dddf.zip | |
Refactor the archivers so that they live in a separate sub-package. Split out
the Pipermail, Prototype, and MailArchiver plugins into separate modules.
Put the archives registry on the config object and initialize it at the right
time.
Update plugin entry points.
Diffstat (limited to 'mailman')
| -rw-r--r-- | mailman/archiving/__init__.py | 31 | ||||
| -rw-r--r-- | mailman/archiving/mailarchive.py | 86 | ||||
| -rw-r--r-- | mailman/archiving/pipermail.py (renamed from mailman/app/archiving.py) | 100 | ||||
| -rw-r--r-- | mailman/archiving/prototype.py | 73 | ||||
| -rw-r--r-- | mailman/configuration.py | 1 | ||||
| -rw-r--r-- | mailman/docs/archivers.txt | 14 | ||||
| -rw-r--r-- | mailman/docs/pipelines.txt | 3 | ||||
| -rw-r--r-- | mailman/initialize.py | 2 | ||||
| -rw-r--r-- | mailman/pipeline/docs/cook-headers.txt | 1 | ||||
| -rw-r--r-- | mailman/tests/test_documentation.py | 3 |
10 files changed, 205 insertions, 109 deletions
diff --git a/mailman/archiving/__init__.py b/mailman/archiving/__init__.py new file mode 100644 index 000000000..b9ef686a1 --- /dev/null +++ b/mailman/archiving/__init__.py @@ -0,0 +1,31 @@ +# Copyright (C) 2008 by the Free Software Foundation, Inc. +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. + +__metaclass__ = type +__all__ = [ + 'initialize', + ] + + +from mailman.app.plugins import get_plugins +from mailman.configuration import config + + +def initialize(): + """Initialize archivers.""" + for archiver in get_plugins('mailman.archiver'): + config.archivers[archiver.name] = archiver diff --git a/mailman/archiving/mailarchive.py b/mailman/archiving/mailarchive.py new file mode 100644 index 000000000..e1405acee --- /dev/null +++ b/mailman/archiving/mailarchive.py @@ -0,0 +1,86 @@ +# Copyright (C) 2008 by the Free Software Foundation, Inc. +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. + +"""The Mail-Archive.com archiver.""" + +__metaclass__ = type +__all__ = [ + 'MailArchive', + ] + + +import hashlib + +from base64 import urlsafe_b64encode +from urllib import quote +from urlparse import urljoin +from zope.interface import implements + +from mailman.configuration import config +from mailman.interfaces.archiver import IArchiver +from mailman.queue import Switchboard + + + +class MailArchive: + """Public archiver at the Mail-Archive.com. + + Messages get archived at http://go.mail-archive.com. + """ + + implements(IArchiver) + + name = 'mail-archive' + is_enabled = False + + @staticmethod + def list_url(mlist): + """See `IArchiver`.""" + if mlist.archive_private: + return None + return urljoin(config.MAIL_ARCHIVE_BASEURL, + quote(mlist.posting_address)) + + @staticmethod + def permalink(mlist, msg): + """See `IArchiver`.""" + if mlist.archive_private: + return None + message_id = msg.get('message-id') + # It is not the archiver's job to ensure the message has a Message-ID. + assert message_id is not None, 'No Message-ID found' + # The angle brackets are not part of the Message-ID. See RFC 2822. + start = (1 if message_id.startswith('<') else 0) + end = (-1 if message_id.endswith('>') else None) + message_id = message_id[start:end] + sha = hashlib.sha1(message_id) + sha.update(str(mlist.post_id)) + message_id_hash = urlsafe_b64encode(sha.digest()) + del msg['x-message-id-hash'] + msg['X-Message-ID-Hash'] = message_id_hash + return urljoin(config.MAIL_ARCHIVE_BASEURL, message_id_hash) + + @staticmethod + def archive_message(mlist, msg): + """See `IArchiver`.""" + if mlist.archive_private: + return + outq = Switchboard(config.OUTQUEUE_DIR) + outq.enqueue( + msg, + listname=mlist.fqdn_listname, + recips=[config.MAIL_ARCHIVE_RECIPIENT]) diff --git a/mailman/app/archiving.py b/mailman/archiving/pipermail.py index 3a8e428d1..1e8f4f28e 100644 --- a/mailman/app/archiving.py +++ b/mailman/archiving/pipermail.py @@ -15,31 +15,24 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, # USA. -"""Application level archiving support.""" +"""Pipermail archiver.""" __metaclass__ = type __all__ = [ 'Pipermail', - 'Prototype', ] import os -import hashlib -from base64 import b32encode, urlsafe_b64encode from cStringIO import StringIO -from email.utils import make_msgid from string import Template -from urllib import quote -from urlparse import urljoin from zope.interface import implements from zope.interface.interface import adapter_hooks from mailman.configuration import config from mailman.interfaces.archiver import IArchiver, IPipermailMailingList from mailman.interfaces.mailinglist import IMailingList -from mailman.queue import Switchboard from mailman.Archiver.HyperArch import HyperArchive @@ -114,94 +107,3 @@ class Pipermail: fileobj.close() # There's no good way to know the url for the archived message. return None - - - -class Prototype: - """A prototype of a third party archiver. - - Mailman proposes a draft specification for interoperability between list - servers and archivers: <http://wiki.list.org/display/DEV/Stable+URLs>. - """ - - implements(IArchiver) - - name = 'prototype' - is_enabled = False - - @staticmethod - def list_url(mlist): - """See `IArchiver`.""" - web_host = config.domains.get(mlist.host_name, mlist.host_name) - return 'http://' + web_host - - @staticmethod - def permalink(mlist, msg): - """See `IArchiver`.""" - message_id = msg.get('message-id') - # It is not the archiver's job to ensure the message has a Message-ID. - assert message_id is not None, 'No Message-ID found' - # The angle brackets are not part of the Message-ID. See RFC 2822. - if message_id.startswith('<') and message_id.endswith('>'): - message_id = message_id[1:-1] - digest = hashlib.sha1(message_id).digest() - message_id_hash = b32encode(digest) - del msg['x-message-id-hash'] - msg['X-Message-ID-Hash'] = message_id_hash - return urljoin(Prototype.list_url(mlist), message_id_hash) - - @staticmethod - def archive_message(mlist, message): - """See `IArchiver`.""" - raise NotImplementedError - - - -class MailArchive: - """Public archiver at the Mail-Archive.com. - - Messages get archived at http://go.mail-archive.com. - """ - - implements(IArchiver) - - name = 'mail-archive' - is_enabled = False - - @staticmethod - def list_url(mlist): - """See `IArchiver`.""" - if mlist.archive_private: - return None - return urljoin(config.MAIL_ARCHIVE_BASEURL, - quote(mlist.posting_address)) - - @staticmethod - def permalink(mlist, msg): - """See `IArchiver`.""" - if mlist.archive_private: - return None - message_id = msg.get('message-id') - # It is not the archiver's job to ensure the message has a Message-ID. - assert message_id is not None, 'No Message-ID found' - # The angle brackets are not part of the Message-ID. See RFC 2822. - start = (1 if message_id.startswith('<') else 0) - end = (-1 if message_id.endswith('>') else None) - message_id = message_id[start:end] - sha = hashlib.sha1(message_id) - sha.update(str(mlist.post_id)) - message_id_hash = urlsafe_b64encode(sha.digest()) - del msg['x-message-id-hash'] - msg['X-Message-ID-Hash'] = message_id_hash - return urljoin(config.MAIL_ARCHIVE_BASEURL, message_id_hash) - - @staticmethod - def archive_message(mlist, msg): - """See `IArchiver`.""" - if mlist.archive_private: - return - outq = Switchboard(config.OUTQUEUE_DIR) - outq.enqueue( - msg, - listname=mlist.fqdn_listname, - recips=[config.MAIL_ARCHIVE_RECIPIENT]) diff --git a/mailman/archiving/prototype.py b/mailman/archiving/prototype.py new file mode 100644 index 000000000..b2efbc5e0 --- /dev/null +++ b/mailman/archiving/prototype.py @@ -0,0 +1,73 @@ +# Copyright (C) 2008 by the Free Software Foundation, Inc. +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. + +"""Prototypical permalinking archiver.""" + +__metaclass__ = type +__all__ = [ + 'Prototype', + ] + + +import hashlib + +from base64 import b32encode +from urlparse import urljoin +from zope.interface import implements + +from mailman.configuration import config +from mailman.interfaces.archiver import IArchiver + + + +class Prototype: + """A prototype of a third party archiver. + + Mailman proposes a draft specification for interoperability between list + servers and archivers: <http://wiki.list.org/display/DEV/Stable+URLs>. + """ + + implements(IArchiver) + + name = 'prototype' + is_enabled = False + + @staticmethod + def list_url(mlist): + """See `IArchiver`.""" + web_host = config.domains.get(mlist.host_name, mlist.host_name) + return 'http://' + web_host + + @staticmethod + def permalink(mlist, msg): + """See `IArchiver`.""" + message_id = msg.get('message-id') + # It is not the archiver's job to ensure the message has a Message-ID. + assert message_id is not None, 'No Message-ID found' + # The angle brackets are not part of the Message-ID. See RFC 2822. + if message_id.startswith('<') and message_id.endswith('>'): + message_id = message_id[1:-1] + digest = hashlib.sha1(message_id).digest() + message_id_hash = b32encode(digest) + del msg['x-message-id-hash'] + msg['X-Message-ID-Hash'] = message_id_hash + return urljoin(Prototype.list_url(mlist), message_id_hash) + + @staticmethod + def archive_message(mlist, message): + """See `IArchiver`.""" + raise NotImplementedError diff --git a/mailman/configuration.py b/mailman/configuration.py index 2f682d114..8a702c457 100644 --- a/mailman/configuration.py +++ b/mailman/configuration.py @@ -174,6 +174,7 @@ class Configuration(object): code = self.DEFAULT_SERVER_LANGUAGE self.languages.enable_language(code) # Create various registries. + self.archivers = {} self.chains = {} self.rules = {} self.handlers = {} diff --git a/mailman/docs/archivers.txt b/mailman/docs/archivers.txt index 56f81b98a..edd82ba1e 100644 --- a/mailman/docs/archivers.txt +++ b/mailman/docs/archivers.txt @@ -24,15 +24,11 @@ Pipermail does not support a permalink, so that interface returns None. Mailman defines a draft spec for how list servers and archivers can interoperate. - >>> from operator import attrgetter - >>> name = attrgetter('name') - >>> from mailman.app.plugins import get_plugins - >>> archivers = {} - >>> for archiver in sorted(get_plugins('mailman.archiver'), key=name): + >>> from mailman.configuration import config + >>> for archiver_name, archiver in sorted(config.archivers.items()): ... print archiver.name ... print ' ', archiver.list_url(mlist) ... print ' ', archiver.permalink(mlist, msg) - ... archivers[archiver.name] = archiver mail-archive http://go.mail-archive.dev/test%40example.com http://go.mail-archive.dev/7ekn-OQjGKjbAsD3StwtnhZ3Azk= @@ -50,7 +46,7 @@ Sending the message to the archiver The archiver is also able to archive the message. >>> mlist.web_page_url = u'http://lists.example.com/' - >>> archivers['pipermail'].archive_message(mlist, msg) + >>> config.archivers['pipermail'].archive_message(mlist, msg) >>> import os >>> from mailman.interfaces.archiver import IPipermailMailingList @@ -62,7 +58,7 @@ The archiver is also able to archive the message. Note however that the prototype archiver can't archive messages. - >>> archivers['prototype'].archive_message(mlist, msg) + >>> config.archivers['prototype'].archive_message(mlist, msg) Traceback (most recent call last): ... NotImplementedError @@ -76,7 +72,7 @@ be used to archive message for free. Mailman comes with a plugin for this archiver; by enabling it messages to public lists will get sent there automatically. - >>> archiver = archivers['mail-archive'] + >>> archiver = config.archivers['mail-archive'] >>> archiver.list_url(mlist) 'http://go.mail-archive.dev/test%40example.com' diff --git a/mailman/docs/pipelines.txt b/mailman/docs/pipelines.txt index 0fe51e0e4..c4f8488a1 100644 --- a/mailman/docs/pipelines.txt +++ b/mailman/docs/pipelines.txt @@ -21,6 +21,8 @@ Processing a message Messages hit the pipeline after they've been accepted for posting. + >>> from mailman.configuration import config + >>> config.archivers['pipermail'].is_enabled = True >>> msg = message_from_string("""\ ... From: aperson@example.com ... To: xtest@example.com @@ -69,7 +71,6 @@ However there are currently no recipients for this message. And the message is now sitting in various other processing queues. >>> from mailman.testing.helpers import get_queue_messages - >>> from mailman.configuration import config >>> messages = get_queue_messages(config.ARCHQUEUE_DIR) >>> len(messages) 1 diff --git a/mailman/initialize.py b/mailman/initialize.py index 6c2a5a8f4..d8dc0d69d 100644 --- a/mailman/initialize.py +++ b/mailman/initialize.py @@ -65,10 +65,12 @@ def initialize_2(debug=False): mailman.configuration.config.db = database # Initialize the rules and chains. Do the imports here so as to avoid # circular imports. + from mailman.archiving import initialize as initialize_archivers from mailman.app.chains import initialize as initialize_chains from mailman.app.rules import initialize as initialize_rules from mailman.app.pipelines import initialize as initialize_pipelines from mailman.app.commands import initialize as initialize_commands + initialize_archivers() initialize_rules() initialize_chains() initialize_pipelines() diff --git a/mailman/pipeline/docs/cook-headers.txt b/mailman/pipeline/docs/cook-headers.txt index d764bd796..4fbdf58bb 100644 --- a/mailman/pipeline/docs/cook-headers.txt +++ b/mailman/pipeline/docs/cook-headers.txt @@ -184,6 +184,7 @@ But normally, a list will include these headers. >>> mlist.include_rfc2369_headers = True >>> mlist.include_list_post_header = True >>> mlist.preferred_language = u'en' + >>> config.archivers['pipermail'].is_enabled = True >>> msg = message_from_string("""\ ... From: aperson@example.com ... Message-ID: <12345> diff --git a/mailman/tests/test_documentation.py b/mailman/tests/test_documentation.py index e805b10fa..48c2c491c 100644 --- a/mailman/tests/test_documentation.py +++ b/mailman/tests/test_documentation.py @@ -79,6 +79,9 @@ def cleaning_teardown(testobj): for message in config.db.message_store.messages: config.db.message_store.delete_message(message['message-id']) config.db.commit() + # Reset all archivers by disabling them. + for archiver in config.archivers.values(): + archiver.is_enabled = False |
