------------------------------------------------------------
revno: 6636
committer: Barry Warsaw <[EMAIL PROTECTED]>
branch nick: 3.0
timestamp: Thu 2008-07-10 22:23:52 -0400
message:
  Merging the Mail-Archive.com branch.
added:
  mailman/archiving/
  mailman/archiving/__init__.py
  mailman/archiving/mailarchive.py
  mailman/archiving/mhonarc.py
  mailman/archiving/prototype.py
renamed:
  mailman/app/archiving.py => mailman/archiving/pipermail.py
modified:
  mailman/Defaults.py
  mailman/bin/testall.py
  mailman/configuration.py
  mailman/docs/archivers.txt
  mailman/docs/pipelines.txt
  mailman/initialize.py
  mailman/interfaces/mailinglist.py
  mailman/loginit.py
  mailman/pipeline/docs/cook-headers.txt
  mailman/queue/docs/outgoing.txt
  mailman/testing/helpers.py
  mailman/testing/smtplistener.py
  mailman/testing/testing.cfg.in
  mailman/tests/test_documentation.py
  setup.py
  mailman/archiving/pipermail.py
    ------------------------------------------------------------
    revno: 6635.1.4
    committer: Barry Warsaw <[EMAIL PROTECTED]>
    branch nick: madotcom
    timestamp: Wed 2008-07-09 22:35:44 -0400
    message:
      Add rough support for a local MHonArc archiver, using the permalink 
proposal
      from the prototype archiver.
      
      Add a mailman.archiver log.
    added:
      mailman/archiving/mhonarc.py
    modified:
      mailman/Defaults.py
      mailman/archiving/mailarchive.py
      mailman/archiving/prototype.py
      mailman/bin/testall.py
      mailman/docs/archivers.txt
      mailman/loginit.py
      setup.py
    ------------------------------------------------------------
    revno: 6635.1.3
    committer: Barry Warsaw <[EMAIL PROTECTED]>
    branch nick: madotcom
    timestamp: Sun 2008-07-06 10:27:05 -0400
    message:
      Use IMailingList.posting_address (a.k.a. List-Post header) in the
      Mail-Archive.com hash calculation, not the post-id sequence number.
      
      test_documentation now starts and stops the smtp listener, simplifing many
      tests.
    modified:
      mailman/archiving/mailarchive.py
      mailman/docs/archivers.txt
      mailman/queue/docs/outgoing.txt
      mailman/tests/test_documentation.py
    ------------------------------------------------------------
    revno: 6635.1.2
    committer: Barry Warsaw <[EMAIL PROTECTED]>
    branch nick: madotcom
    timestamp: Sat 2008-07-05 11:06:37 -0400
    message:
      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.
    added:
      mailman/archiving/
      mailman/archiving/__init__.py
      mailman/archiving/mailarchive.py
      mailman/archiving/prototype.py
    renamed:
      mailman/app/archiving.py => mailman/archiving/pipermail.py
    modified:
      mailman/configuration.py
      mailman/docs/archivers.txt
      mailman/docs/pipelines.txt
      mailman/initialize.py
      mailman/pipeline/docs/cook-headers.txt
      mailman/tests/test_documentation.py
      setup.py
      mailman/archiving/pipermail.py
    ------------------------------------------------------------
    revno: 6635.1.1
    committer: Barry Warsaw <[EMAIL PROTECTED]>
    branch nick: madotcom
    timestamp: Sat 2008-07-05 10:41:12 -0400
    message:
      Implement the basic Mail-Archive.com archiving algorithm.
      
      Fix the start up of the SMTPServer to listen on a consistent port number.
      post_number -> post_id
    modified:
      mailman/Defaults.py
      mailman/app/archiving.py
      mailman/bin/testall.py
      mailman/docs/archivers.txt
      mailman/interfaces/mailinglist.py
      mailman/testing/helpers.py
      mailman/testing/smtplistener.py
      mailman/testing/testing.cfg.in
      setup.py

=== modified file 'mailman/Defaults.py'
--- a/mailman/Defaults.py       2008-03-31 02:55:22 +0000
+++ b/mailman/Defaults.py       2008-07-10 02:35:44 +0000
@@ -220,6 +220,22 @@
 # - $fqdn_listname  -- the long name of the list being accessed
 PUBLIC_ARCHIVE_URL = 'http://$hostname/pipermail/$fqdn_listname'
 
+# The public Mail-Archive.com service's base url.
+MAIL_ARCHIVE_BASEURL = 'http://go.mail-archive.com/'
+# The posting address for the Mail-Archive.com service
+MAIL_ARCHIVE_RECIPIENT = '[EMAIL PROTECTED]'
+
+# The command for archiving to a local MHonArc instance.
+MHONARC_COMMAND = """\
+/usr/bin/mhonarc \
+-add \
+-dbfile $PRIVATE_ARCHIVE_FILE_DIR/${listname}.mbox/mhonarc.db \
+-outdir $VAR_DIR/mhonarc/${listname} \
+-stderr $LOG_DIR/mhonarc \
+-stdout $LOG_DIR/mhonarc \
+-spammode \
+-umask 022"""
+
 # Are archives on or off by default?
 DEFAULT_ARCHIVE = On
 

=== added directory 'mailman/archiving'
=== added file 'mailman/archiving/__init__.py'
--- a/mailman/archiving/__init__.py     1970-01-01 00:00:00 +0000
+++ b/mailman/archiving/__init__.py     2008-07-05 15:06:37 +0000
@@ -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

=== added file 'mailman/archiving/mailarchive.py'
--- a/mailman/archiving/mailarchive.py  1970-01-01 00:00:00 +0000
+++ b/mailman/archiving/mailarchive.py  2008-07-10 02:35:44 +0000
@@ -0,0 +1,87 @@
+# 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.
+        if message_id.startswith('<') and message_id.endswith('>'):
+            message_id = message_id[1:-1]
+        else:
+            message_id = message_id.strip()
+        sha = hashlib.sha1(message_id)
+        sha.update(str(mlist.posting_address))
+        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])

=== added file 'mailman/archiving/mhonarc.py'
--- a/mailman/archiving/mhonarc.py      1970-01-01 00:00:00 +0000
+++ b/mailman/archiving/mhonarc.py      2008-07-10 02:35:44 +0000
@@ -0,0 +1,95 @@
+# 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.
+
+"""MHonArc archiver."""
+
+__metaclass__ = type
+__all__ = [
+    'MHonArc',
+    ]
+
+
+import hashlib
+import logging
+import subprocess
+
+from base64 import b32encode
+from string import Template
+from urlparse import urljoin
+from zope.interface import implements
+
+from mailman.configuration import config
+from mailman.interfaces.archiver import IArchiver
+
+
+log = logging.getLogger('mailman.archiver')
+
+
+
+class MHonArc:
+    """Local MHonArc archiver."""
+
+    implements(IArchiver)
+
+    name = 'mhonarc'
+    is_enabled = False
+
+    @staticmethod
+    def list_url(mlist):
+        """See `IArchiver`."""
+        # XXX What about private MHonArc archives?
+        web_host = config.domains.get(mlist.host_name, mlist.host_name)
+        return Template(config.PUBLIC_ARCHIVE_URL).safe_substitute(
+            listname=mlist.fqdn_listname,
+            hostname=web_host,
+            fqdn_listname=mlist.fqdn_listname,
+            )
+
+    @staticmethod
+    def permalink(mlist, msg):
+        """See `IArchiver`."""
+        # XXX What about private MHonArc archives?
+        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]
+        else:
+            message_id = message_id.strip()
+        sha = hashlib.sha1(message_id)
+        message_id_hash = b32encode(sha.digest())
+        del msg['x-message-id-hash']
+        msg['X-Message-ID-Hash'] = message_id_hash
+        return urljoin(MHonArc.list_url(mlist), message_id_hash)
+
+    @staticmethod
+    def archive_message(mlist, msg):
+        """See `IArchiver`."""
+        substitutions = config.__dict__.copy()
+        substitutions['listname'] = mlist.fqdn_listname
+        command = Template(config.MHONARC_COMMAND).safe_substitute(
+            substitutions)
+        proc = subprocess.Popen(
+            command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+            shell=True)
+        stdout, stderr = proc.communicate(msg.as_string())
+        if proc.returncode <> 0:
+            log.error('%s: mhonarc subprocess had non-zero exit code: %s' %
+                      (msg['message-id'], proc.returncode))
+        log.info(stdout)
+        log.error(stderr)

=== renamed file 'mailman/app/archiving.py' => 'mailman/archiving/pipermail.py'
--- a/mailman/app/archiving.py  2008-07-03 02:29:20 +0000
+++ b/mailman/archiving/pipermail.py    2008-07-05 15:06:37 +0000
@@ -15,23 +15,18 @@
 # 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
 from cStringIO import StringIO
-from email.utils import make_msgid
 from string import Template
-from urlparse import urljoin
 from zope.interface import implements
 from zope.interface.interface import adapter_hooks
 
@@ -79,7 +74,7 @@
     implements(IArchiver)
 
     name = 'pipermail'
-    is_enabled = True
+    is_enabled = False
 
     @staticmethod
     def list_url(mlist):
@@ -112,43 +107,3 @@
         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

=== added file 'mailman/archiving/prototype.py'
--- a/mailman/archiving/prototype.py    1970-01-01 00:00:00 +0000
+++ b/mailman/archiving/prototype.py    2008-07-10 02:35:44 +0000
@@ -0,0 +1,75 @@
+# 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]
+        else:
+            message_id = message_id.strip()
+        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

=== modified file 'mailman/bin/testall.py'
--- a/mailman/bin/testall.py    2008-07-03 19:32:25 +0000
+++ b/mailman/bin/testall.py    2008-07-10 02:35:44 +0000
@@ -31,9 +31,11 @@
 import unittest
 import pkg_resources
 
+from mailman import Defaults
 from mailman.configuration import config
 from mailman.i18n import _
 from mailman.initialize import initialize_1, initialize_2
+from mailman.testing.helpers import SMTPServer
 from mailman.version import MAILMAN_VERSION
 
 
@@ -226,10 +228,14 @@
         with open(cfg_out, 'a') as fp:
             print >> fp, 'VAR_DIR = "%s"' % var_dir
             print >> fp, 'MAILMAN_USER = "%s"' % user_name
-            print >> fp, 'MAILMAN_UID = %d' % user_id
+            print >> fp, 'MAILMAN_UID =', user_id
             print >> fp, 'MAILMAN_GROUP = "%s"' % group_name
-            print >> fp, 'MAILMAN_GID = %d' % group_id
+            print >> fp, 'MAILMAN_GID =', group_id
             print >> fp, "LANGUAGES = 'en'"
+            print >> fp, 'SMTPPORT =', SMTPServer.port
+            # A fake MHonArc command, for testing.
+            print >> fp, 'MHONARC_COMMAND = """/bin/echo', \
+                  Defaults.MHONARC_COMMAND, '"""'
 
         initialize_1(cfg_out, propagate_logs=parser.options.stderr)
         mailman_uid = pwd.getpwnam(config.MAILMAN_USER).pw_uid

=== modified file 'mailman/configuration.py'
--- a/mailman/configuration.py  2008-04-26 05:39:14 +0000
+++ b/mailman/configuration.py  2008-07-05 15:06:37 +0000
@@ -174,6 +174,7 @@
         code = self.DEFAULT_SERVER_LANGUAGE
         self.languages.enable_language(code)
         # Create various registries.
+        self.archivers = {}
         self.chains = {}
         self.rules = {}
         self.handlers = {}

=== modified file 'mailman/docs/archivers.txt'
--- a/mailman/docs/archivers.txt        2008-07-03 02:29:20 +0000
+++ b/mailman/docs/archivers.txt        2008-07-10 02:35:44 +0000
@@ -1,4 +1,5 @@
-= Archivers =
+Archivers
+=========
 
 Mailman supports pluggable archivers, and it comes with several default
 archivers.
@@ -23,15 +24,17 @@
 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/ZaXPPxRMM9_hFZL4vTRlQlBx8pc=
+    mhonarc
+        http://www.example.com/.../[EMAIL PROTECTED]
+        http://www.example.com/.../RSZCG7IGPHFIRW3EMTVMMDNJMNCVCOLE
     pipermail
         http://www.example.com/pipermail/[EMAIL PROTECTED]
         None
@@ -40,12 +43,13 @@
         http://www.example.com/RSZCG7IGPHFIRW3EMTVMMDNJMNCVCOLE
 
 
-== Sending the message to the archiver ==
+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
@@ -57,7 +61,123 @@
 
 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
+
+
+The Mail-Archive.com
+--------------------
+
+The Mail-Archive <http://www.mail-archive.com> is a public archiver that can
+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 = config.archivers['mail-archive']
+    >>> archiver.list_url(mlist)
+    'http://go.mail-archive.dev/test%40example.com'
+    >>> archiver.permalink(mlist, msg)
+    'http://go.mail-archive.dev/ZaXPPxRMM9_hFZL4vTRlQlBx8pc='
+
+To archive the message, the archiver actually mails the message to a special
+address at the Mail-Archive.
+
+    >>> archiver.archive_message(mlist, msg)
+
+    >>> from mailman.queue.outgoing import OutgoingRunner
+    >>> from mailman.testing.helpers import make_testable_runner
+    >>> outgoing = make_testable_runner(OutgoingRunner)
+    >>> outgoing.run()
+
+    >>> from operator import itemgetter
+    >>> messages = list(smtpd.messages)
+    >>> len(messages)
+    1
+
+    >>> print messages[0].as_string()
+    From: [EMAIL PROTECTED]
+    To: [EMAIL PROTECTED]
+    Subject: An archived message
+    Message-ID: <12345>
+    X-Message-ID-Hash: ZaXPPxRMM9_hFZL4vTRlQlBx8pc=
+    MIME-Version: 1.0
+    Content-Type: text/plain; charset="us-ascii"
+    Content-Transfer-Encoding: 7bit
+    Sender: [EMAIL PROTECTED]
+    Errors-To: [EMAIL PROTECTED]
+    X-Peer: 127.0.0.1:...
+    X-MailFrom: [EMAIL PROTECTED]
+    X-RcptTo: [EMAIL PROTECTED]
+    <BLANKLINE>
+    Here is an archived message.
+    _______________________________________________
+    Test mailing list
+    [EMAIL PROTECTED]
+    http://lists.example.com/listinfo/[EMAIL PROTECTED]
+
+    >>> smtpd.clear()
+
+However, if the mailing list is not public, the message will never be archived
+at this service.
+
+    >>> mlist.archive_private = True
+    >>> print archiver.list_url(mlist)
+    None
+    >>> print archiver.permalink(mlist, msg)
+    None
+    >>> archiver.archive_message(mlist, msg)
+    >>> list(smtpd.messages)
+    []
+
+Additionally, this archiver can handle malformed Message-IDs.
+
+    >>> mlist.archive_private = False
+    >>> del msg['message-id']
+    >>> msg['Message-ID'] = '12345>'
+    >>> archiver.permalink(mlist, msg)
+    'http://go.mail-archive.dev/bXvG32YzcDEIVDaDLaUSVQekfo8='
+
+    >>> del msg['message-id']
+    >>> msg['Message-ID'] = '<12345'
+    >>> archiver.permalink(mlist, msg)
+    'http://go.mail-archive.dev/9rockPrT1Mm-jOsLWS6_hseR_OY='
+
+    >>> del msg['message-id']
+    >>> msg['Message-ID'] = '12345'
+    >>> archiver.permalink(mlist, msg)
+    'http://go.mail-archive.dev/ZaXPPxRMM9_hFZL4vTRlQlBx8pc='
+
+    >>> del msg['message-id']
+    >>> msg['Message-ID'] = '    12345    '
+    >>> archiver.permalink(mlist, msg)
+    'http://go.mail-archive.dev/ZaXPPxRMM9_hFZL4vTRlQlBx8pc='
+
+
+MHonArc
+-------
+
+The MHonArc archiver <http://www.mhonarc.org> is also available.
+
+    >>> archiver = config.archivers['mhonarc']
+    >>> archiver.name
+    'mhonarc'
+
+Messages sent to a local MHonArc instance are added to its archive via a
+subprocess call.
+
+    >>> archiver.archive_message(mlist, msg)
+    >>> archive_log = open(os.path.join(config.LOG_DIR, 'archiver'))
+    >>> try:
+    ...     contents = archive_log.read()
+    ... finally:
+    ...     archive_log.close()
+    >>> print 'LOG:', contents
+    LOG: ... /usr/bin/mhonarc -add
+        -dbfile /.../private/[EMAIL PROTECTED]/mhonarc.db
+        -outdir /.../mhonarc/[EMAIL PROTECTED]
+        -stderr /.../logs/mhonarc
+        -stdout /.../logs/mhonarc
+        -spammode -umask 022
+        ...

=== modified file 'mailman/docs/pipelines.txt'
--- a/mailman/docs/pipelines.txt        2008-07-03 19:32:25 +0000
+++ b/mailman/docs/pipelines.txt        2008-07-05 15:06:37 +0000
@@ -21,6 +21,8 @@
 
 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: [EMAIL PROTECTED]
     ... To: [EMAIL PROTECTED]
@@ -69,7 +71,6 @@
 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

=== modified file 'mailman/initialize.py'
--- a/mailman/initialize.py     2008-04-26 05:39:14 +0000
+++ b/mailman/initialize.py     2008-07-05 15:06:37 +0000
@@ -65,10 +65,12 @@
     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()

=== modified file 'mailman/interfaces/mailinglist.py'
--- a/mailman/interfaces/mailinglist.py 2008-03-27 09:14:14 +0000
+++ b/mailman/interfaces/mailinglist.py 2008-07-05 14:41:12 +0000
@@ -147,7 +147,7 @@
     last_post_date = Attribute(
         """The date and time a message was last posted to the mailing list.""")
 
-    post_number = Attribute(
+    post_id = Attribute(
         """A monotonically increasing integer sequentially assigned to each
         list posting.""")
 

=== modified file 'mailman/loginit.py'
--- a/mailman/loginit.py        2008-03-12 22:35:16 +0000
+++ b/mailman/loginit.py        2008-07-10 02:35:44 +0000
@@ -35,6 +35,7 @@
 DATEFMT = '%b %d %H:%M:%S %Y'
 
 LOGGERS = (
+    'archiver',     # All archiver output
     'bounce',       # All bounce processing logs go here
     'config',       # Configuration issues
     'debug',        # Only used for development

=== modified file 'mailman/pipeline/docs/cook-headers.txt'
--- a/mailman/pipeline/docs/cook-headers.txt    2008-07-03 02:29:20 +0000
+++ b/mailman/pipeline/docs/cook-headers.txt    2008-07-05 15:06:37 +0000
@@ -184,6 +184,7 @@
     >>> 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: [EMAIL PROTECTED]
     ... Message-ID: <12345>

=== modified file 'mailman/queue/docs/outgoing.txt'
--- a/mailman/queue/docs/outgoing.txt   2008-07-03 19:32:25 +0000
+++ b/mailman/queue/docs/outgoing.txt   2008-07-06 14:27:05 +0000
@@ -26,21 +26,12 @@
     ...            u'password', DeliveryMode.regular, u'en',
     ...            ack=False, admin_notif=False)
 
-    >>> from mailman.testing.helpers import SMTPServer
-    >>> smtpd = SMTPServer()
-    >>> smtpd.start()
-    >>> from mailman.configuration import config
-    >>> old_host = config.SMTPHOST
-    >>> old_port = config.SMTPPORT
-    >>> config.SMTPHOST = smtpd.host
-    >>> config.SMTPPORT = smtpd.port
-
 By setting the mailing list to personalize messages, each recipient will get a
 unique copy of the message, with certain headers tailored for that recipient.
 
     >>> from mailman.interfaces import Personalization
     >>> mlist.personalize = Personalization.individual
-    >>> config.db.commit()
+    >>> commit()
 
     >>> msg = message_from_string("""\
     ... From: [EMAIL PROTECTED]
@@ -55,6 +46,7 @@
 been processed by the rule set and pipeline.  But we can simulate that here by
 injecting a message directly into the outgoing queue.
 
+    >>> from mailman.configuration import config
     >>> msgdata = {}
     >>> handler = config.handlers['calculate-recipients']
     >>> handler.process(mlist, msg, msgdata)
@@ -86,11 +78,3 @@
     [EMAIL PROTECTED]
     [EMAIL PROTECTED]
     [EMAIL PROTECTED]
-
-
-Clean up
---------
-
-    >>> smtpd.stop()
-    >>> config.SMTPHOST = old_host
-    >>> config.SMTPPORT = old_port

=== modified file 'mailman/testing/helpers.py'
--- a/mailman/testing/helpers.py        2008-07-03 19:32:25 +0000
+++ b/mailman/testing/helpers.py        2008-07-05 14:41:12 +0000
@@ -162,7 +162,7 @@
     """An smtp server for testing."""
 
     host = 'localhost'
-    port = 9025
+    port = 10825
 
     def __init__(self):
         self._messages = []
@@ -174,6 +174,10 @@
         """Start the smtp server in a thread."""
         log.info('test SMTP server starting')
         self._thread.start()
+        smtpd = smtplib.SMTP()
+        smtpd.connect(self.host, self.port)
+        smtpd.helo('test.localhost')
+        smtpd.quit()
 
     def stop(self):
         """Stop the smtp server."""

=== modified file 'mailman/testing/smtplistener.py'
--- a/mailman/testing/smtplistener.py   2008-07-03 19:32:25 +0000
+++ b/mailman/testing/smtplistener.py   2008-07-05 14:41:12 +0000
@@ -58,12 +58,13 @@
 
     def __init__(self, localaddr, queue):
         smtpd.SMTPServer.__init__(self, localaddr, None)
+        log.info('[SMTPServer] listening: %s', localaddr)
         self._queue = queue
 
     def handle_accept(self):
         """Handle connections by creating our own Channel object."""
         conn, addr = self.accept()
-        log.info('accepted: %s', addr)
+        log.info('[SMTPServer] accepted: %s', addr)
         Channel(self, conn, addr)
 
     def process_message(self, peer, mailfrom, rcpttos, data):
@@ -72,7 +73,8 @@
         message['X-Peer'] = '%s:%s' % peer
         message['X-MailFrom'] = mailfrom
         message['X-RcptTo'] = COMMASPACE.join(rcpttos)
-        log.info('processed message: %s', message.get('message-id', 'n/a'))
+        log.info('[SMTPServer] processed message: %s',
+                 message.get('message-id', 'n/a'))
         self._queue.put(message)
 
     def start(self):

=== modified file 'mailman/testing/testing.cfg.in'
--- a/mailman/testing/testing.cfg.in    2008-07-03 19:32:25 +0000
+++ b/mailman/testing/testing.cfg.in    2008-07-05 14:41:12 +0000
@@ -4,11 +4,14 @@
 # both the process running the tests and all sub-processes (e.g. qrunners)
 # must share the same configuration file.
 
-SMTPPORT                = 10825
 MAX_RESTARTS            = 1
 MTA                     = None
 USE_LMTP                = Yes
 
+# Make sure these goes to fake domains.
+MAIL_ARCHIVE_BASEURL = 'http://go.mail-archive.dev/'
+MAIL_ARCHIVE_RECIPIENT = '[EMAIL PROTECTED]'
+
 add_domain('example.com', 'www.example.com')
 
 # bin/testall will add additional runtime configuration variables here.

=== modified file 'mailman/tests/test_documentation.py'
--- a/mailman/tests/test_documentation.py       2008-03-13 01:52:43 +0000
+++ b/mailman/tests/test_documentation.py       2008-07-06 14:27:05 +0000
@@ -29,6 +29,7 @@
 from mailman.Message import Message
 from mailman.app.styles import style_manager
 from mailman.configuration import config
+from mailman.testing.helpers import SMTPServer
 
 
 DOT = '.'
@@ -54,12 +55,15 @@
 
 def setup(testobj):
     """Test setup."""
+    smtpd = SMTPServer()
+    smtpd.start()
     # In general, I don't like adding convenience functions, since I think
     # doctests should do the imports themselves.  It makes for better
     # documentation that way.  However, a few are really useful, or help to
     # hide some icky test implementation details.
     testobj.globs['message_from_string'] = specialized_message_from_string
     testobj.globs['commit'] = config.db.commit
+    testobj.globs['smtpd'] = smtpd
 
 
 
@@ -79,6 +83,13 @@
     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
+    # Shutdown the smtp server.
+    smtpd = testobj.globs['smtpd']
+    smtpd.clear()
+    smtpd.stop()
 
 
 

=== modified file 'setup.py'
--- a/setup.py  2008-07-03 02:29:20 +0000
+++ b/setup.py  2008-07-10 02:35:44 +0000
@@ -91,10 +91,12 @@
         '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.archiving.mailarchive:MailArchive',
+            'mhonarc = mailman.archiving.mhonarc:MHonArc',
+            'pipermail = mailman.archiving.pipermail:Pipermail',
+            'prototype = mailman.archiving.prototype:Prototype',
             ],
-        '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',



--
Primary development focus
https://code.launchpad.net/~mailman-coders/mailman/3.0

You are receiving this branch notification because you are subscribed to it.
_______________________________________________
Mailman-checkins mailing list
[email protected]
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to