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 | |
| parent | dda21fc619518344dfc44081c64674f6374fe404 (diff) | |
| download | mailman-08400a46afb740a0e49058707969462fa7e7dddf.tar.gz mailman-08400a46afb740a0e49058707969462fa7e7dddf.tar.zst mailman-08400a46afb740a0e49058707969462fa7e7dddf.zip | |
| -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 | ||||
| -rw-r--r-- | setup.py | 8 |
11 files changed, 209 insertions, 113 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 @@ -91,11 +91,11 @@ Any other spelling is incorrect.""", 'console_scripts': list(scripts), # Entry point for plugging in different database backends. 'mailman.archiver' : [ - 'pipermail = mailman.app.archiving:Pipermail', - 'prototype = mailman.app.archiving:Prototype', - 'mail-archive = mailman.app.archiving:MailArchive', + 'pipermail = mailman.archiving.pipermail:Pipermail', + 'prototype = mailman.archiving.prototype:Prototype', + 'mail-archive = mailman.archiving.mailarchive:MailArchive', ], - 'mailman.scrubber' : 'stock = mailman.app.archiving:Pipermail', + 'mailman.scrubber' : 'stock = mailman.archiving.pipermail:Pipermail', 'mailman.commands' : list(commands), 'mailman.database' : 'stock = mailman.database:StockDatabase', 'mailman.mta' : 'stock = mailman.MTA:Manual', |
