diff options
| author | Barry Warsaw | 2008-12-25 23:57:07 -0500 |
|---|---|---|
| committer | Barry Warsaw | 2008-12-25 23:57:07 -0500 |
| commit | b8e68e7577aa12e0e355aabe2845981f0d73e3b5 (patch) | |
| tree | 4d988c8de9e29b080ac258d0bc1e4eee8e5ee32b | |
| parent | d4de7996e6d4fb5db04dfed3b3fd12747622b164 (diff) | |
| download | mailman-b8e68e7577aa12e0e355aabe2845981f0d73e3b5.tar.gz mailman-b8e68e7577aa12e0e355aabe2845981f0d73e3b5.tar.zst mailman-b8e68e7577aa12e0e355aabe2845981f0d73e3b5.zip | |
30 files changed, 183 insertions, 98 deletions
diff --git a/buildout.cfg b/buildout.cfg index d4ba46351..e4f46a0fc 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -27,3 +27,6 @@ recipe = zc.recipe.testrunner eggs = mailman defaults = '--tests-pattern ^tests --exit-with-status'.split() +# Hack in extra arguments to zope.testrunner. +initialization = from mailman.testing.layers import ConfigLayer; + ConfigLayer.hack_options_parser() diff --git a/mailman/Archiver/HyperArch.py b/mailman/Archiver/HyperArch.py index 0395f6482..a4dc13423 100644 --- a/mailman/Archiver/HyperArch.py +++ b/mailman/Archiver/HyperArch.py @@ -43,6 +43,7 @@ from email.Header import decode_header, make_header from locknix.lockfile import Lock from string import Template +from mailman import Defaults from mailman import Utils from mailman import i18n from mailman.Archiver import HyperDatabase @@ -50,6 +51,7 @@ from mailman.Archiver import pipermail from mailman.Mailbox import ArchiverMailbox from mailman.config import config + log = logging.getLogger('mailman.error') # Set up i18n. Assume the current language has already been set in the caller. @@ -174,7 +176,7 @@ def quick_maketext(templatefile, dict=None, lang=None, mlist=None): listname = mlist.fqdn_listname if lang is None: if mlist is None: - lang = config.DEFAULT_SERVER_LANGUAGE + lang = Defaults.DEFAULT_SERVER_LANGUAGE else: lang = mlist.preferred_language cachekey = (templatefile, lang, listname) @@ -250,7 +252,7 @@ class Article(pipermail.Article): self._lang = lang self._mlist = mlist - if config.ARCHIVER_OBSCURES_EMAILADDRS: + if Defaults.ARCHIVER_OBSCURES_EMAILADDRS: # Avoid i18n side-effects. Note that the language for this # article (for this list) could be different from the site-wide # preferred language, so we need to ensure no side-effects will @@ -325,7 +327,7 @@ class Article(pipermail.Article): if hasattr(self, '_mlist'): self._lang = self._mlist.preferred_language else: - self._lang = config.DEFAULT_SERVER_LANGUAGE + self._lang = Defaults.DEFAULT_SERVER_LANGUAGE if not d.has_key('cenc'): self.cenc = None if not d.has_key('decoded'): @@ -357,7 +359,7 @@ class Article(pipermail.Article): if email: self.decoded['email'] = email if subject: - if config.ARCHIVER_OBSCURES_EMAILADDRS: + if Defaults.ARCHIVER_OBSCURES_EMAILADDRS: with i18n.using_language(self._lang): atmark = _(' at ') subject = re.sub(r'([-+,.\w]+)@([-+.\w]+)', @@ -405,7 +407,7 @@ class Article(pipermail.Article): d["subject_html"] = self.quote(self.subject) d["subject_url"] = url_quote(self.subject) d["in_reply_to_url"] = url_quote(self.in_reply_to) - if config.ARCHIVER_OBSCURES_EMAILADDRS: + if Defaults.ARCHIVER_OBSCURES_EMAILADDRS: # Point the mailto url back to the list author = re.sub('@', _(' at '), self.author) emailurl = self._mlist.posting_address @@ -509,7 +511,7 @@ class Article(pipermail.Article): # Coerce the body to Unicode and replace any invalid characters. if not isinstance(body, unicode): body = unicode(body, cset, 'replace') - if config.ARCHIVER_OBSCURES_EMAILADDRS: + if Defaults.ARCHIVER_OBSCURES_EMAILADDRS: with i18n.using_language(self._lang): atmark = _(' at ') body = re.sub(r'([-+,.\w]+)@([-+.\w]+)', @@ -705,7 +707,7 @@ class HyperArchive(pipermail.T): # The TOC is always in the charset of the list's preferred language d['meta'] += html_charset % Utils.GetCharSet(mlist.preferred_language) # The site can disable public access to the mbox file. - if config.PUBLIC_MBOX: + if Defaults.PUBLIC_MBOX: template = 'archtoc.html' else: template = 'archtocnombox.html' @@ -962,7 +964,7 @@ class HyperArchive(pipermail.T): def write_index_entry(self, article): subject = self.get_header("subject", article) author = self.get_header("author", article) - if config.ARCHIVER_OBSCURES_EMAILADDRS: + if Defaults.ARCHIVER_OBSCURES_EMAILADDRS: try: author = re.sub('@', _(' at '), author) except UnicodeError: @@ -1038,7 +1040,7 @@ class HyperArchive(pipermail.T): def update_archive(self, archive): self.__super_update_archive(archive) # only do this if the gzip module was imported globally, and - # gzip'ing was enabled via config.GZIP_ARCHIVE_TXT_FILES. See + # gzip'ing was enabled via Defaults.GZIP_ARCHIVE_TXT_FILES. See # above. if gzip: archz = None @@ -1135,7 +1137,7 @@ class HyperArchive(pipermail.T): if j != -1 and (j < k or k == -1): text = jr.group(1) length = len(text) - if config.ARCHIVER_OBSCURES_EMAILADDRS: + if Defaults.ARCHIVER_OBSCURES_EMAILADDRS: text = re.sub('@', atmark, text) URL = self.maillist.GetScriptURL( 'listinfo', absolute=1) diff --git a/mailman/Defaults.py b/mailman/Defaults.py index a0aad0b8e..4f4f7981e 100644 --- a/mailman/Defaults.py +++ b/mailman/Defaults.py @@ -1051,7 +1051,7 @@ DEFAULT_DIGEST_FOOTER = DEFAULT_MSG_FOOTER DEFAULT_DIGEST_IS_DEFAULT = No DEFAULT_MIME_IS_DEFAULT_DIGEST = No -DEFAULT_DIGEST_SIZE_THRESHHOLD = 30 # KB +DEFAULT_DIGEST_SIZE_THRESHOLD = 30 # KB DEFAULT_DIGEST_SEND_PERIODIC = Yes # Headers which should be kept in both RFC 1153 (plain) and MIME digests. RFC diff --git a/mailman/Mailbox.py b/mailman/Mailbox.py index cbc38585a..bb4982fc6 100644 --- a/mailman/Mailbox.py +++ b/mailman/Mailbox.py @@ -26,8 +26,8 @@ from email.Errors import MessageParseError from email.Generator import Generator from email.Parser import Parser +from mailman import Defaults from mailman.Message import Message -from mailman.config import config @@ -91,9 +91,10 @@ class ArchiverMailbox(Mailbox): # scrub() method, giving the scrubber module a chance to do its thing # before the message is archived. def __init__(self, fp, mlist): - if config.ARCHIVE_SCRUBBER: - __import__(config.ARCHIVE_SCRUBBER) - self._scrubber = sys.modules[config.ARCHIVE_SCRUBBER].process + scrubber_module = Defaults.ARCHIVE_SCRUBBER + if scrubber_module: + __import__(scrubber_module) + self._scrubber = sys.modules[scrubber_module].process else: self._scrubber = None self._mlist = mlist diff --git a/mailman/Message.py b/mailman/Message.py index c8fb17f3b..07d7f15bb 100644 --- a/mailman/Message.py +++ b/mailman/Message.py @@ -264,8 +264,7 @@ class UserNotification(Message): def _enqueue(self, mlist, **_kws): # Not imported at module scope to avoid import loop - from mailman.queue import Switchboard - virginq = Switchboard(config.VIRGINQUEUE_DIR) + virginq = config.switchboards['virgin'] # The message metadata better have a 'recip' attribute. enqueue_kws = dict( recips=self.recips, @@ -298,8 +297,7 @@ class OwnerNotification(UserNotification): def _enqueue(self, mlist, **_kws): # Not imported at module scope to avoid import loop - from mailman.queue import Switchboard - virginq = Switchboard(config.VIRGINQUEUE_DIR) + virginq = config.switchboards['virgin'] # The message metadata better have a `recip' attribute virginq.enqueue(self, listname=mlist.fqdn_listname, diff --git a/mailman/archiving/mailarchive.py b/mailman/archiving/mailarchive.py index 3edeb8c39..ded402ab4 100644 --- a/mailman/archiving/mailarchive.py +++ b/mailman/archiving/mailarchive.py @@ -32,7 +32,6 @@ from zope.interface import implements from mailman.config import config from mailman.interfaces.archiver import IArchiver -from mailman.queue import Switchboard @@ -73,15 +72,13 @@ class MailArchive: 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) + return urljoin(config.archiver.mail_archive.base_url, 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]) + if not mlist.archive_private: + config.switchboards['out'].enqueue( + msg, + listname=mlist.fqdn_listname, + recips=[config.archiver.mail_archive.recipient]) diff --git a/mailman/archiving/mhonarc.py b/mailman/archiving/mhonarc.py index 3d17ffd13..c20c34ba0 100644 --- a/mailman/archiving/mhonarc.py +++ b/mailman/archiving/mhonarc.py @@ -32,6 +32,7 @@ from string import Template from urlparse import urljoin from zope.interface import implements +from mailman import Defaults from mailman.config import config from mailman.interfaces.archiver import IArchiver @@ -53,7 +54,7 @@ class MHonArc: """See `IArchiver`.""" # XXX What about private MHonArc archives? web_host = config.domains[mlist.host_name].url_host - return Template(config.PUBLIC_ARCHIVE_URL).safe_substitute( + return Template(Defaults.PUBLIC_ARCHIVE_URL).safe_substitute( listname=mlist.fqdn_listname, hostname=web_host, fqdn_listname=mlist.fqdn_listname, @@ -82,7 +83,7 @@ class MHonArc: """See `IArchiver`.""" substitutions = config.__dict__.copy() substitutions['listname'] = mlist.fqdn_listname - command = Template(config.MHONARC_COMMAND).safe_substitute( + command = Template(Defaults.MHONARC_COMMAND).safe_substitute( substitutions) proc = subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, diff --git a/mailman/archiving/pipermail.py b/mailman/archiving/pipermail.py index f42f064ed..ea3ccef5e 100644 --- a/mailman/archiving/pipermail.py +++ b/mailman/archiving/pipermail.py @@ -30,6 +30,8 @@ from string import Template from zope.interface import implements from zope.interface.interface import adapter_hooks +from mailman import Defaults +from mailman.Utils import makedirs from mailman.config import config from mailman.interfaces.archiver import IArchiver, IPipermailMailingList from mailman.interfaces.mailinglist import IMailingList @@ -55,7 +57,10 @@ class PipermailMailingListAdapter: basedir = config.PRIVATE_ARCHIVE_FILE_DIR else: basedir = config.PUBLIC_ARCHIVE_FILE_DIR - return os.path.join(basedir, self._mlist.fqdn_listname) + # Make sure the archive directory exists. + archive_dir = os.path.join(basedir, self._mlist.fqdn_listname) + makedirs(archive_dir) + return archive_dir def adapt_mailing_list_for_pipermail(iface, obj): @@ -91,7 +96,7 @@ class Pipermail: url = mlist.script_url('private') + '/index.html' else: web_host = config.domains[mlist.host_name].url_host - url = Template(config.PUBLIC_ARCHIVE_URL).safe_substitute( + url = Template(config.archiver.pipermail.base_url).safe_substitute( listname=mlist.fqdn_listname, hostname=web_host, fqdn_listname=mlist.fqdn_listname, diff --git a/mailman/core/logging.py b/mailman/core/logging.py index ed400215b..8c1463be9 100644 --- a/mailman/core/logging.py +++ b/mailman/core/logging.py @@ -19,6 +19,13 @@ from __future__ import absolute_import +__metaclass__ = type +__all__ = [ + 'initialize', + 'reopen', + ] + + import os import sys import codecs @@ -75,22 +82,35 @@ class ReopenableFileHandler(logging.Handler): -def initialize(propagate=False): +def initialize(propagate=None): + """Initialize all logs. + + :param propagate: Flag specifying whether logs should propagate their + messages to the root logger. If omitted, propagation is determined + from the configuration files. + :type propagate: bool or None + """ # First, find the root logger and configure the logging subsystem. - # Initialize the root logger, then create a formatter for all the sublogs. + # Initialize the root logger, then create a formatter for all the + # sublogs. The root logger should log to stderr. logging.basicConfig(format=config.logging.root.format, datefmt=config.logging.root.datefmt, - level=as_log_level(config.logging.root.level)) - # Create the subloggers + level=as_log_level(config.logging.root.level), + stream=sys.stderr) + # Create the subloggers. for logger_config in config.logger_configs: - logger_name = 'mailman.' + logger_config.name.split('.')[-1] + sub_name = logger_config.name.split('.')[-1] + if sub_name == 'root': + continue + logger_name = 'mailman.' + sub_name log = logging.getLogger(logger_name) # Get settings from log configuration file (or defaults). log_format = logger_config.format log_datefmt = logger_config.datefmt # Propagation to the root logger is how we handle logging to stderr # when the qrunners are not run as a subprocess of mailmanctl. - log.propagate = as_boolean(logger_config.propagate) + log.propagate = (as_boolean(logger_config.propagate) + if propagate is None else propagate) # Set the logger's level. log.setLevel(as_log_level(logger_config.level)) # Create a formatter for this logger, then a handler, and link the @@ -106,5 +126,6 @@ def initialize(propagate=False): def reopen(): + """Re-open all log files.""" for handler in _handlers: handler.reopen() diff --git a/mailman/core/styles.py b/mailman/core/styles.py index 76428a955..b8fff8278 100644 --- a/mailman/core/styles.py +++ b/mailman/core/styles.py @@ -85,7 +85,8 @@ class DefaultStyle: mlist.welcome_msg = u'' mlist.goodbye_msg = u'' mlist.subscribe_policy = Defaults.DEFAULT_SUBSCRIBE_POLICY - mlist.subscribe_auto_approval = Defaults.DEFAULT_SUBSCRIBE_AUTO_APPROVAL + mlist.subscribe_auto_approval = ( + Defaults.DEFAULT_SUBSCRIBE_AUTO_APPROVAL) mlist.unsubscribe_policy = Defaults.DEFAULT_UNSUBSCRIBE_POLICY mlist.private_roster = Defaults.DEFAULT_PRIVATE_ROSTER mlist.obscure_addresses = Defaults.DEFAULT_OBSCURE_ADDRESSES @@ -98,7 +99,8 @@ class DefaultStyle: mlist.pass_mime_types = Defaults.DEFAULT_PASS_MIME_TYPES mlist.filter_filename_extensions = ( Defaults.DEFAULT_FILTER_FILENAME_EXTENSIONS) - mlist.pass_filename_extensions = Defaults.DEFAULT_PASS_FILENAME_EXTENSIONS + mlist.pass_filename_extensions = ( + Defaults.DEFAULT_PASS_FILENAME_EXTENSIONS) mlist.filter_content = Defaults.DEFAULT_FILTER_CONTENT mlist.collapse_alternatives = Defaults.DEFAULT_COLLAPSE_ALTERNATIVES mlist.convert_html_to_plaintext = ( @@ -108,13 +110,13 @@ class DefaultStyle: mlist.digestable = Defaults.DEFAULT_DIGESTABLE mlist.digest_is_default = Defaults.DEFAULT_DIGEST_IS_DEFAULT mlist.mime_is_default_digest = Defaults.DEFAULT_MIME_IS_DEFAULT_DIGEST - mlist.digest_size_threshhold = Defaults.DEFAULT_DIGEST_SIZE_THRESHHOLD + mlist.digest_size_threshold = Defaults.DEFAULT_DIGEST_SIZE_THRESHOLD mlist.digest_send_periodic = Defaults.DEFAULT_DIGEST_SEND_PERIODIC mlist.digest_header = Defaults.DEFAULT_DIGEST_HEADER mlist.digest_footer = Defaults.DEFAULT_DIGEST_FOOTER - mlist.digest_volume_frequency = Defaults.DEFAULT_DIGEST_VOLUME_FREQUENCY + mlist.digest_volume_frequency = ( + Defaults.DEFAULT_DIGEST_VOLUME_FREQUENCY) mlist.one_last_digest = {} - mlist.digest_members = {} mlist.next_digest_number = 1 mlist.nondigestable = Defaults.DEFAULT_NONDIGESTABLE mlist.personalize = Personalization.none @@ -174,7 +176,8 @@ class DefaultStyle: # Bounces mlist.bounce_processing = Defaults.DEFAULT_BOUNCE_PROCESSING mlist.bounce_score_threshold = Defaults.DEFAULT_BOUNCE_SCORE_THRESHOLD - mlist.bounce_info_stale_after = Defaults.DEFAULT_BOUNCE_INFO_STALE_AFTER + mlist.bounce_info_stale_after = ( + Defaults.DEFAULT_BOUNCE_INFO_STALE_AFTER) mlist.bounce_you_are_disabled_warnings = ( Defaults.DEFAULT_BOUNCE_YOU_ARE_DISABLED_WARNINGS) mlist.bounce_you_are_disabled_warnings_interval = ( diff --git a/mailman/docs/bounces.txt b/mailman/docs/bounces.txt index 6d0fad688..9e8bcd23b 100644 --- a/mailman/docs/bounces.txt +++ b/mailman/docs/bounces.txt @@ -30,8 +30,7 @@ Bounce a message by passing in the original message, and an optional error message. The bounced message ends up in the virgin queue, awaiting sending to the original messageauthor. - >>> from mailman.queue import Switchboard - >>> switchboard = Switchboard(config.VIRGINQUEUE_DIR) + >>> switchboard = config.switchboards['virgin'] >>> from mailman.app.bounces import bounce_message >>> bounce_message(mlist, msg) >>> len(switchboard.files) diff --git a/mailman/docs/pipelines.txt b/mailman/docs/pipelines.txt index 5a84bb4b9..94cc792cd 100644 --- a/mailman/docs/pipelines.txt +++ b/mailman/docs/pipelines.txt @@ -70,7 +70,7 @@ 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 - >>> messages = get_queue_messages(config.ARCHQUEUE_DIR) + >>> messages = get_queue_messages('archive') >>> len(messages) 1 >>> print messages[0].msg.as_string() @@ -104,13 +104,13 @@ And the message is now sitting in various other processing queues. This mailing list is not linked to an NNTP newsgroup, so there's nothing in the outgoing nntp queue. - >>> messages = get_queue_messages(config.NEWSQUEUE_DIR) + >>> messages = get_queue_messages('news') >>> len(messages) 0 This is the message that will actually get delivered to end recipients. - >>> messages = get_queue_messages(config.OUTQUEUE_DIR) + >>> messages = get_queue_messages('out') >>> len(messages) 1 >>> print messages[0].msg.as_string() diff --git a/mailman/pipeline/cleanse_dkim.py b/mailman/pipeline/cleanse_dkim.py index bc76e2726..3f3be98f8 100644 --- a/mailman/pipeline/cleanse_dkim.py +++ b/mailman/pipeline/cleanse_dkim.py @@ -31,7 +31,7 @@ __all__ = ['CleanseDKIM'] from zope.interface import implements -from mailman.config import config +from mailman import Defaults from mailman.i18n import _ from mailman.interfaces import IHandler @@ -47,7 +47,7 @@ class CleanseDKIM: def process(self, mlist, msg, msgdata): """See `IHandler`.""" - if config.REMOVE_DKIM_HEADERS: + if Defaults.REMOVE_DKIM_HEADERS: del msg['domainkey-signature'] del msg['dkim-signature'] del msg['authentication-results'] diff --git a/mailman/pipeline/decorate.py b/mailman/pipeline/decorate.py index bc684a1b1..f150d6d0c 100644 --- a/mailman/pipeline/decorate.py +++ b/mailman/pipeline/decorate.py @@ -28,6 +28,7 @@ from email.MIMEText import MIMEText from string import Template from zope.interface import implements +from mailman import Defaults from mailman import Utils from mailman.Message import Message from mailman.config import config @@ -204,7 +205,7 @@ def decorate(mlist, template, extradict=None): web_page_url = mlist.web_page_url, description = mlist.description, info = mlist.info, - cgiext = config.CGIEXT, + cgiext = Defaults.CGIEXT, ) if extradict is not None: d.update(extradict) diff --git a/mailman/pipeline/docs/acknowledge.txt b/mailman/pipeline/docs/acknowledge.txt index ccbb70e79..d1206b6f3 100644 --- a/mailman/pipeline/docs/acknowledge.txt +++ b/mailman/pipeline/docs/acknowledge.txt @@ -15,8 +15,7 @@ acknowledgment. >>> # Ensure that the virgin queue is empty, since we'll be checking this >>> # for new auto-response messages. - >>> from mailman.queue import Switchboard - >>> virginq = Switchboard(config.VIRGINQUEUE_DIR) + >>> virginq = config.switchboards['virgin'] >>> virginq.files [] diff --git a/mailman/pipeline/docs/archives.txt b/mailman/pipeline/docs/archives.txt index 9595a36e0..d81f6e27b 100644 --- a/mailman/pipeline/docs/archives.txt +++ b/mailman/pipeline/docs/archives.txt @@ -8,10 +8,9 @@ archivers to work in a separate process from the main Mailman delivery processes. >>> from mailman.app.lifecycle import create_list - >>> from mailman.queue import Switchboard >>> handler = config.handlers['to-archive'] >>> mlist = create_list(u'_xtest@example.com') - >>> switchboard = Switchboard(config.ARCHQUEUE_DIR) + >>> switchboard = config.switchboards['archive'] A helper function. diff --git a/mailman/pipeline/docs/digests.txt b/mailman/pipeline/docs/digests.txt index f478c1ec0..1d7112fd5 100644 --- a/mailman/pipeline/docs/digests.txt +++ b/mailman/pipeline/docs/digests.txt @@ -7,14 +7,13 @@ digests, although only two are currently supported: MIME digests and RFC 1153 (a.k.a. plain text) digests. >>> from mailman.pipeline.to_digest import process - >>> from mailman.queue import Switchboard >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist.preferred_language = u'en' >>> mlist.web_page_url = u'http://www.example.com/' >>> mlist.real_name = u'XTest' >>> mlist.subject_prefix = u'[_XTest] ' >>> mlist.one_last_digest = set() - >>> switchboard = Switchboard(config.VIRGINQUEUE_DIR) + >>> switchboard = config.switchboards['virgin'] This is a helper function used to iterate through all the accumulated digest messages, in the order in which they were posted. This makes it easier to @@ -406,11 +405,16 @@ When messages come in with a content-type character set different than that of the list's preferred language, recipients wil get an internationalized digest. French is not enabled by default site-wide, so enable that now. -XXX We also have to set the default server language to French, otherwise the -English template will be found and the masthead won't be translated. - >>> config.languages.enable_language('fr') - >>> config.DEFAULT_SERVER_LANGUAGE = u'fr' + + # Simulate the site administrator setting the default server language to + # French in the configuration file. Without this, the English template + # will be found and the masthead won't be translated. + >>> config.push('french', """ + ... [mailman] + ... default_language: fr + ... """) + >>> mlist.preferred_language = u'fr' >>> msg = message_from_string("""\ ... From: aperson@example.org diff --git a/mailman/pipeline/docs/nntp.txt b/mailman/pipeline/docs/nntp.txt index 3ef3b2413..0120de394 100644 --- a/mailman/pipeline/docs/nntp.txt +++ b/mailman/pipeline/docs/nntp.txt @@ -5,11 +5,10 @@ Mailman has an NNTP gateway, whereby messages posted to the mailing list can be forwarded onto an NNTP newsgroup. Typically this means Usenet, but since NNTP is to Usenet as IP is to the web, it's more general than that. - >>> from mailman.queue import Switchboard >>> handler = config.handlers['to-usenet'] >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist.preferred_language = u'en' - >>> switchboard = Switchboard(config.NEWSQUEUE_DIR) + >>> switchboard = config.switchboards['news'] Gatewaying from the mailing list to the newsgroup happens through a separate 'nntp' queue and happen immediately when the message is posted through to the diff --git a/mailman/pipeline/docs/replybot.txt b/mailman/pipeline/docs/replybot.txt index 7325417ed..f9f824e4e 100644 --- a/mailman/pipeline/docs/replybot.txt +++ b/mailman/pipeline/docs/replybot.txt @@ -13,8 +13,7 @@ message or the amount of time since the last auto-response. >>> # Ensure that the virgin queue is empty, since we'll be checking this >>> # for new auto-response messages. - >>> from mailman.queue import Switchboard - >>> virginq = Switchboard(config.VIRGINQUEUE_DIR) + >>> virginq = config.switchboards['virgin'] >>> virginq.files [] diff --git a/mailman/pipeline/docs/tagger.txt b/mailman/pipeline/docs/tagger.txt index 64996755b..9f0bcd4b2 100644 --- a/mailman/pipeline/docs/tagger.txt +++ b/mailman/pipeline/docs/tagger.txt @@ -9,7 +9,6 @@ its Subject: and Keywords: headers compared against these regular expressions. The message then gets tagged with the topic names of each hit. >>> from mailman.pipeline.tagger import process - >>> from mailman.queue import Switchboard >>> mlist = config.db.list_manager.create(u'_xtest@example.com') Topics must be enabled for Mailman to do any topic matching, even if topics diff --git a/mailman/pipeline/docs/to-outgoing.txt b/mailman/pipeline/docs/to-outgoing.txt index 70f93cfae..046ed9be8 100644 --- a/mailman/pipeline/docs/to-outgoing.txt +++ b/mailman/pipeline/docs/to-outgoing.txt @@ -9,10 +9,9 @@ term somewhat incorrectly, but within the spirit of the standard, which basically describes how to encode the recipient's address in the originator headers for unambigous bounce processing. - >>> from mailman.queue import Switchboard >>> handler = config.handlers['to-outgoing'] >>> mlist = config.db.list_manager.create(u'_xtest@example.com') - >>> switchboard = Switchboard(config.OUTQUEUE_DIR) + >>> switchboard = config.switchboards['out'] >>> def queue_size(): ... size = len(switchboard.files) diff --git a/mailman/pipeline/scrubber.py b/mailman/pipeline/scrubber.py index abc904613..e8268f0cf 100644 --- a/mailman/pipeline/scrubber.py +++ b/mailman/pipeline/scrubber.py @@ -38,8 +38,8 @@ from locknix.lockfile import Lock from mimetypes import guess_all_extensions from zope.interface import implements +from mailman import Defaults from mailman import Utils -from mailman.config import config from mailman.core.errors import DiscardMessage from mailman.core.plugins import get_plugin from mailman.i18n import _ @@ -158,7 +158,7 @@ def replace_payload_by_text(msg, text, charset): def process(mlist, msg, msgdata=None): - sanitize = config.ARCHIVE_HTML_SANITIZER + sanitize = Defaults.ARCHIVE_HTML_SANITIZER outer = True if msgdata is None: msgdata = {} @@ -394,7 +394,7 @@ def makedirs(dir): def save_attachment(mlist, msg, dir, filter_html=True): - fsdir = os.path.join(config.PRIVATE_ARCHIVE_FILE_DIR, + fsdir = os.path.join(Defaults.PRIVATE_ARCHIVE_FILE_DIR, mlist.fqdn_listname, dir) makedirs(fsdir) # Figure out the attachment type and get the decoded data @@ -409,7 +409,7 @@ def save_attachment(mlist, msg, dir, filter_html=True): filename, fnext = os.path.splitext(filename) # For safety, we should confirm this is valid ext for content-type # but we can use fnext if we introduce fnext filtering - if config.SCRUBBER_USE_ATTACHMENT_FILENAME_EXTENSION: + if Defaults.SCRUBBER_USE_ATTACHMENT_FILENAME_EXTENSION: # HTML message doesn't have filename :-( ext = fnext or guess_extension(ctype, fnext) else: @@ -430,7 +430,7 @@ def save_attachment(mlist, msg, dir, filter_html=True): with Lock(os.path.join(fsdir, 'attachments.lock')): # Now base the filename on what's in the attachment, uniquifying it if # necessary. - if not filename or config.SCRUBBER_DONT_USE_ATTACHMENT_FILENAME: + if not filename or Defaults.SCRUBBER_DONT_USE_ATTACHMENT_FILENAME: filebase = 'attachment' else: # Sanitize the filename given in the message headers @@ -475,7 +475,7 @@ def save_attachment(mlist, msg, dir, filter_html=True): try: fp.write(decodedpayload) fp.close() - cmd = config.ARCHIVE_HTML_SANITIZER % {'filename' : tmppath} + cmd = Defaults.ARCHIVE_HTML_SANITIZER % {'filename' : tmppath} progfp = os.popen(cmd, 'r') decodedpayload = progfp.read() status = progfp.close() diff --git a/mailman/pipeline/to_archive.py b/mailman/pipeline/to_archive.py index 4d8c27cf1..6ecb860c2 100644 --- a/mailman/pipeline/to_archive.py +++ b/mailman/pipeline/to_archive.py @@ -26,7 +26,6 @@ from zope.interface import implements from mailman.config import config from mailman.i18n import _ from mailman.interfaces import IHandler -from mailman.queue import Switchboard @@ -48,7 +47,5 @@ class ToArchive: # presence. I'm keeping "X-Archive: no" for backwards compatibility. if 'x-no-archive' in msg or msg.get('x-archive', '').lower() == 'no': return - # Send the message to the archiver queue - archq = Switchboard(config.ARCHQUEUE_DIR) - # Send the message to the queue - archq.enqueue(msg, msgdata) + # Send the message to the archiver queue. + config.switchboards['archive'].enqueue(msg, msgdata) diff --git a/mailman/pipeline/to_digest.py b/mailman/pipeline/to_digest.py index 78984c92e..42f92df4e 100644 --- a/mailman/pipeline/to_digest.py +++ b/mailman/pipeline/to_digest.py @@ -48,6 +48,7 @@ from email.parser import Parser from email.utils import formatdate, getaddresses, make_msgid from zope.interface import implements +from mailman import Defaults from mailman import Message from mailman import Utils from mailman import i18n @@ -58,7 +59,6 @@ from mailman.core import errors from mailman.interfaces import DeliveryMode, DeliveryStatus, IHandler from mailman.pipeline.decorate import decorate from mailman.pipeline.scrubber import process as scrubber -from mailman.queue import Switchboard _ = i18n._ @@ -268,8 +268,8 @@ def send_i18n_digests(mlist, mboxfp): # for the specific MIME or plain digests. keeper = {} all_keepers = {} - for header in (config.MIME_DIGEST_KEEP_HEADERS + - config.PLAIN_DIGEST_KEEP_HEADERS): + for header in (Defaults.MIME_DIGEST_KEEP_HEADERS + + Defaults.PLAIN_DIGEST_KEEP_HEADERS): all_keepers[header] = True all_keepers = all_keepers.keys() for keep in all_keepers: @@ -325,7 +325,7 @@ def send_i18n_digests(mlist, mboxfp): print >> plainmsg, _('[Message discarded by content filter]') continue # Honor the default setting - for h in config.PLAIN_DIGEST_KEEP_HEADERS: + for h in Defaults.PLAIN_DIGEST_KEEP_HEADERS: if msg[h]: uh = Utils.wrap('%s: %s' % (h, Utils.oneline(msg[h], in_unicode=True))) @@ -378,7 +378,7 @@ def send_i18n_digests(mlist, mboxfp): # Do our final bit of housekeeping, and then send each message to the # outgoing queue for delivery. mlist.next_digest_number += 1 - virginq = Switchboard(config.VIRGINQUEUE_DIR) + virginq = config.switchboards['virgin'] # Calculate the recipients lists plainrecips = set() mimerecips = set() diff --git a/mailman/pipeline/to_outgoing.py b/mailman/pipeline/to_outgoing.py index d8d1ec935..bbaf43301 100644 --- a/mailman/pipeline/to_outgoing.py +++ b/mailman/pipeline/to_outgoing.py @@ -28,10 +28,10 @@ __all__ = ['ToOutgoing'] from zope.interface import implements +from mailman import Defaults from mailman.config import config from mailman.i18n import _ from mailman.interfaces import IHandler, Personalization -from mailman.queue import Switchboard @@ -45,7 +45,7 @@ class ToOutgoing: def process(self, mlist, msg, msgdata): """See `IHandler`.""" - interval = config.VERP_DELIVERY_INTERVAL + interval = Defaults.VERP_DELIVERY_INTERVAL # Should we VERP this message? If personalization is enabled for this # list and VERP_PERSONALIZED_DELIVERIES is true, then yes we VERP it. # Also, if personalization is /not/ enabled, but @@ -57,7 +57,7 @@ class ToOutgoing: if 'verp' in msgdata: pass elif mlist.personalize <> Personalization.none: - if config.VERP_PERSONALIZED_DELIVERIES: + if Defaults.VERP_PERSONALIZED_DELIVERIES: msgdata['verp'] = True elif interval == 0: # Never VERP @@ -69,5 +69,5 @@ class ToOutgoing: # VERP every `interval' number of times msgdata['verp'] = not (int(mlist.post_id) % interval) # And now drop the message in qfiles/out - outq = Switchboard(config.OUTQUEUE_DIR) - outq.enqueue(msg, msgdata, listname=mlist.fqdn_listname) + config.switchboards['out'].enqueue( + msg, msgdata, listname=mlist.fqdn_listname) diff --git a/mailman/queue/archive.py b/mailman/queue/archive.py index e9fd5f7ad..7725f6f97 100644 --- a/mailman/queue/archive.py +++ b/mailman/queue/archive.py @@ -25,6 +25,7 @@ __all__ = [ import os import time +import logging from datetime import datetime from email.Utils import parsedate_tz, mktime_tz, formatdate @@ -34,6 +35,8 @@ from mailman import Defaults from mailman.core.plugins import get_plugins from mailman.queue import Runner +log = logging.getLogger('mailman.error') + class ArchiveRunner(Runner): @@ -78,5 +81,10 @@ class ArchiveRunner(Runner): # While a list archiving lock is acquired, archive the message. with Lock(os.path.join(mlist.data_path, 'archive.lck')): for archive_factory in get_plugins('mailman.archiver'): - archive_factory().archive_message(mlist, msg) - + # A problem in one archiver should not prevent any other + # archiver from running. + try: + archive = archive_factory() + archive.archive_message(mlist, msg) + except Exception: + log.exception('Broken archiver: %s' % archive.name) diff --git a/mailman/testing/helpers.py b/mailman/testing/helpers.py index 2f5b4af01..62b0d8d95 100644 --- a/mailman/testing/helpers.py +++ b/mailman/testing/helpers.py @@ -80,15 +80,14 @@ class _Bag: setattr(self, key, value) -def get_queue_messages(queue): +def get_queue_messages(queue_name): """Return and clear all the messages in the given queue. - :param queue: An ISwitchboard or a string naming a queue. + :param queue_name: A string naming a queue. :return: A list of 2-tuples where each item contains the message and message metadata. """ - if isinstance(queue, basestring): - queue = Switchboard(queue) + queue = config.switchboards[queue_name] messages = [] for filebase in queue.files: msg, msgdata = queue.dequeue(filebase) diff --git a/mailman/testing/layers.py b/mailman/testing/layers.py index bda18289c..883a5c784 100644 --- a/mailman/testing/layers.py +++ b/mailman/testing/layers.py @@ -25,7 +25,9 @@ __all__ = [ import os +import sys import shutil +import logging import tempfile from pkg_resources import resource_string @@ -33,6 +35,7 @@ from textwrap import dedent from mailman.config import config from mailman.core.initialize import initialize +from mailman.i18n import _ from mailman.testing.helpers import SMTPServer @@ -59,9 +62,27 @@ class ConfigLayer: [mailman] var_dir: %s """ % cls.var_dir) - # Read the testing config, but don't push it yet. + # Read the testing config and push it. test_config += resource_string('mailman.testing', 'testing.cfg') config.push('test config', test_config) + # Enable log message propagation. + for logger_config in config.logger_configs: + sub_name = logger_config.name.split('.')[-1] + if sub_name == 'root': + continue + logger_name = 'mailman.' + sub_name + log = logging.getLogger(logger_name) + log.propagate = True + log.setLevel(logging.DEBUG) + # zope.testing sets up logging before we get to our own initialization + # function. This messes with the root logger, so explicitly set it to + # go to stderr. + if cls.stderr: + console = logging.StreamHandler(sys.stderr) + formatter = logging.Formatter(config.logging.root.format, + config.logging.root.datefmt) + console.setFormatter(formatter) + logging.getLogger().addHandler(console) @classmethod def tearDown(cls): @@ -87,6 +108,27 @@ class ConfigLayer: config.db.message_store.delete_message(message['message-id']) config.db.commit() + # Flag to indicate that loggers should propagate to the console. + stderr = False + + @classmethod + def handle_stderr(cls, *ignore): + cls.stderr = True + + @classmethod + def hack_options_parser(cls): + """Hack our way into the zc.testing framework. + + Add our custom command line option parsing into zc.testing's. We do + the imports here so that if zc.testing isn't invoked, this stuff never + gets in the way. This is pretty fragile, depend on changes in the + zc.testing package. There should be a better way! + """ + from zope.testing.testrunner.options import parser + parser.add_option('-e', '--stderr', + action='callback', callback=cls.handle_stderr, + help=_('Propagate log errors to stderr.')) + class SMTPLayer(ConfigLayer): diff --git a/mailman/testing/testing.cfg b/mailman/testing/testing.cfg index baac7d803..8bb720ab7 100644 --- a/mailman/testing/testing.cfg +++ b/mailman/testing/testing.cfg @@ -60,7 +60,18 @@ max_restarts: 1 base_url: http://go.mail-archive.dev/ recipient: archive@mail-archive.dev +[archiver.pipermail] +base_url: http://www.example.com/pipermail/$listname + [domain.example_dot_com] email_host: example.com base_url: http://www.example.com contact_address: postmaster@example.com + +[language.ja] +description: Japanese +charset: euc-jp + +[language.fr] +description: French +charset: iso-8859-1 @@ -90,7 +90,6 @@ case second `m'. Any other spelling is incorrect.""", include_package_data = True, entry_points = { 'console_scripts': list(scripts), - # Entry point for plugging in different database backends. 'mailman.archiver' : [ 'mail-archive = mailman.archiving.mailarchive:MailArchive', 'mhonarc = mailman.archiving.mhonarc:MHonArc', |
