 Mailman/Archiver/Archiver.py       |   29 +++--
 Mailman/Archiver/HyperArch.py      |   18 ++-
 Mailman/Archiver/pipermail.py      |    7 +
 Mailman/Bouncers/BouncerAPI.py     |   12 +-
 Mailman/Bouncers/DSN.py            |   10 -
 Mailman/Bouncers/Qmail.py          |   18 ---
 Mailman/Bouncers/SMTP32.py         |    8 -
 Mailman/Bouncers/SimpleMatch.py    |   58 ++--------
 Mailman/Bouncers/SimpleWarning.py  |   12 --
 Mailman/Cgi/admin.py               |    5 
 Mailman/Cgi/admindb.py             |    2 
 Mailman/Cgi/confirm.py             |    2 
 Mailman/Cgi/edithtml.py            |    2 
 Mailman/Cgi/listinfo.py            |    2 
 Mailman/Cgi/options.py             |    2 
 Mailman/Cgi/private.py             |    6 -
 Mailman/Cgi/rmlist.py              |    5 
 Mailman/Cgi/roster.py              |    2 
 Mailman/Cgi/subscribe.py           |    2 
 Mailman/Defaults.py.in             |    6 -
 Mailman/Deliverer.py               |    5 
 Mailman/Gui/Digest.py              |    3 
 Mailman/Gui/General.py             |    2 
 Mailman/Gui/Topics.py              |    5 
 Mailman/HTMLFormatter.py           |    3 
 Mailman/Handlers/Cleanse.py        |   11 +
 Mailman/Handlers/CookHeaders.py    |   14 +-
 Mailman/Handlers/Decorate.py       |   15 +-
 Mailman/Handlers/Hold.py           |    3 
 Mailman/Handlers/SMTPDirect.py     |    2 
 Mailman/Handlers/Scrubber.py       |   24 ++--
 Mailman/Handlers/SpamDetect.py     |   19 +--
 Mailman/Handlers/ToArchive.py      |   13 +-
 Mailman/Handlers/ToDigest.py       |   17 ++
 Mailman/ListAdmin.py               |    4 
 Mailman/MTA/Manual.py              |    2 
 Mailman/MTA/Postfix.py             |  127 ++++++++++++++++++----
 Mailman/MTA/Utils.py               |   18 +--
 Mailman/MailList.py                |   53 ++++++---
 Mailman/Message.py                 |    2 
 Mailman/Queue/ArchRunner.py        |    2 
 Mailman/Queue/BounceRunner.py      |   21 ---
 Mailman/Queue/NewsRunner.py        |    8 -
 Mailman/SecurityManager.py         |    8 +
 Mailman/Site.py                    |   78 ++++++++++---
 Mailman/Utils.py                   |   49 ++++++++
 Mailman/Version.py                 |    6 -
 Mailman/i18n.py                    |   15 --
 NEWS                               |   34 -----
 bin/cleanarch                      |    7 -
 bin/list_lists                     |    7 -
 bin/mailmanctl                     |    9 -
 bin/newlist                        |   22 +++
 bin/qrunner                        |   16 --
 bin/show_qfiles                    |   23 ---
 bin/update                         |    1 
 contrib/README.mmdsr               |   64 ++++-------
 contrib/mmdsr                      |  118 +-------------------
 messages/es/LC_MESSAGES/mailman.po |  213 ++++++++++++++++++++-----------------
 misc/Makefile.in                   |    7 -
 tests/test_bounces.py              |   44 +------
 61 files changed, 673 insertions(+), 629 deletions(-)

diff -pru mailman-cvs-2.1-maint/bin/cleanarch mailman-2.1.7.cg/bin/cleanarch
--- mailman-cvs-2.1-maint/bin/cleanarch	2006-01-22 21:34:20.000000000 +0100
+++ mailman-2.1.7.cg/bin/cleanarch	2005-08-27 03:40:16.000000000 +0200
@@ -1,6 +1,6 @@
 #! @PYTHON@
 
-# Copyright (C) 2001-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2003 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
@@ -14,8 +14,7 @@
 #
 # 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.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
 """Clean up an .mbox archive file.
 
@@ -60,7 +59,7 @@ cre = re.compile(mailbox.UnixMailbox._fr
 # From RFC 2822, a header field name must contain only characters from 33-126
 # inclusive, excluding colon.  I.e. from oct 41 to oct 176 less oct 072.  Must
 # use re.match() so that it's anchored at the beginning of the line.
-fre = re.compile(r'[\041-\071\073-\176]+')
+fre = re.compile(r'[\041-\071\073-\0176]+')
 
 
 
diff -pru mailman-cvs-2.1-maint/bin/list_lists mailman-2.1.7.cg/bin/list_lists
--- mailman-cvs-2.1-maint/bin/list_lists	2005-08-27 03:40:16.000000000 +0200
+++ mailman-2.1.7.cg/bin/list_lists	2006-02-22 10:28:36.000000000 +0100
@@ -108,13 +108,16 @@ def main():
     if not bare:
         print len(mlists), _('matching mailing lists found:')
 
-    format = '%%%ds - %%.%ds' % (longest, 77 - longest)
+    format = '%%%ds%%s - %%.%ds' % (longest, 77 - longest)
     for mlist in mlists:
         if bare:
             print mlist.internal_name()
         else:
             description = mlist.description or _('[no description available]')
-            print '   ', format % (mlist.real_name, description)
+            if '@' in mlist.internal_name():
+                print '   ', format % (mlist.real_name, '@', description)
+            else:
+                print '   ', format % (mlist.real_name, ' ', description)
 
 
 
diff -pru mailman-cvs-2.1-maint/bin/mailmanctl mailman-2.1.7.cg/bin/mailmanctl
--- mailman-cvs-2.1-maint/bin/mailmanctl	2006-02-05 05:45:09.000000000 +0100
+++ mailman-2.1.7.cg/bin/mailmanctl	2005-08-27 03:40:16.000000000 +0200
@@ -1,6 +1,6 @@
 #! @PYTHON@
 
-# Copyright (C) 2001-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2004 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
@@ -14,8 +14,7 @@
 #
 # 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.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
 """Primary start-up and shutdown script for Mailman's qrunner daemon.
 
@@ -419,8 +418,8 @@ def main():
         os.setsid()
         # Instead of cd'ing to root, cd to the Mailman installation home
         os.chdir(mm_cfg.PREFIX)
-        # Set our file mode creation umask
-        os.umask(007)
+        # Clear our file mode creation umask
+        os.umask(0)
         # I don't think we have any unneeded file descriptors.
         #
         # Now start all the qrunners.  This returns a dictionary where the
diff -pru mailman-cvs-2.1-maint/bin/newlist mailman-2.1.7.cg/bin/newlist
--- mailman-cvs-2.1-maint/bin/newlist	2005-08-27 03:40:16.000000000 +0200
+++ mailman-2.1.7.cg/bin/newlist	2006-03-10 12:52:10.000000000 +0100
@@ -73,6 +73,9 @@ where `mydom.ain' is the mail domain nam
 urlhost is not in the virtual host list, then mm_cfg.DEFAULT_EMAIL_HOST will
 be used for the email interface.
 
+NDIM XXX Ooops. How to keep this backward compatibility below? The
+vhost branch newlist is currently following a different model.
+
 For backward compatibility, you can also specify the domain to create your
 new list in by spelling the listname like so:
 
@@ -154,12 +157,23 @@ def main():
     else:
         listname = raw_input(_('Enter the name of the list: '))
     listname = listname.lower()
-
+    
     if '@' in listname:
         # note that --urlhost and --emailhost have precedence
-        listname, domain = listname.split('@', 1)
-        urlhost = urlhost or domain
-        emailhost = emailhost or mm_cfg.VIRTUAL_HOSTS.get(domain, domain)
+        firstname, domain = listname.split('@', 1)
+        urlhost = urlhost or ('lists.%s' % domain) # TODO: Not configurable yet
+        if not emailhost:
+            emailhost = domain
+        else:
+            usage(1, _('You cannot use an email host different from the domain part of the list name.'))
+        if not mm_cfg.VIRTUAL_HOSTS.has_key(urlhost):
+            _urlhost = repr(urlhost)
+            _emailhost = repr(emailhost)
+            usage(1, _('The Mailman config file lacks a line mapping urlhost to emailhost:\n  add_virtualhost(%(_urlhost)s,\n                  %(_emailhost)s)'))
+    else:
+        # Handle site wide lists (non-vhost lists, i.e. without '@' in their name)
+        # in the traditional manner.
+        pass
 
     urlhost = urlhost or mm_cfg.DEFAULT_URL_HOST
     host_name = emailhost or \
diff -pru mailman-cvs-2.1-maint/bin/qrunner mailman-2.1.7.cg/bin/qrunner
--- mailman-cvs-2.1-maint/bin/qrunner	2006-01-19 02:07:40.000000000 +0100
+++ mailman-2.1.7.cg/bin/qrunner	2005-08-27 03:40:16.000000000 +0200
@@ -1,6 +1,6 @@
 #! @PYTHON@
 
-# Copyright (C) 2001-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2001,2002 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
@@ -14,8 +14,7 @@
 #
 # 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.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
 """Run one or more qrunners, once or repeatedly.
 
@@ -67,9 +66,6 @@ Options:
 
 runner is required unless -l or -h is given, and it must be one of the names
 displayed by the -l switch.
-
-Note also that this script should be started up from mailmanctl as a normal
-operation.  It is only useful for debugging if it is run separately.
 """
 
 import sys
@@ -88,6 +84,8 @@ COMMASPACE = ', '
 # Flag which says whether we're running under mailmanctl or not.
 AS_SUBPROC = 0
 
+LogStdErr('error', 'qrunner', manual_reprime=0)
+
 
 
 def usage(code, msg=''):
@@ -214,12 +212,6 @@ def main():
     if len(runners) == 0:
         usage(1, _('No runner name given.'))
 
-    # Before we startup qrunners, we redirect the stderr to mailman syslog.
-    # We assume !AS_SUBPROC is running for debugging purpose and don't
-    # log errors in mailman logs/error but keep printing to stderr.
-    if AS_SUBPROC:
-        LogStdErr('error', 'qrunner', manual_reprime=0, tee_to_real_stderr=0)
-
     # Fast track for one infinite runner
     if len(runners) == 1 and not once:
         qrunner = make_qrunner(*runners[0])
diff -pru mailman-cvs-2.1-maint/bin/show_qfiles mailman-2.1.7.cg/bin/show_qfiles
--- mailman-cvs-2.1-maint/bin/show_qfiles	2006-03-10 11:36:50.000000000 +0100
+++ mailman-2.1.7.cg/bin/show_qfiles	2003-09-22 04:29:50.000000000 +0200
@@ -1,21 +1,4 @@
 #! @PYTHON@
-#
-# Copyright (C) 2006 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.
 
 """Show the contents of one or more Mailman queue files.
 
@@ -81,11 +64,7 @@ def main():
         fp = open(filename)
         if filename.endswith(".pck"):
             msg = load(fp)
-            data = load(fp)
-            if data.get('_parsemsg'):
-                sys.stdout.write(msg)
-            else:
-                sys.stdout.write(msg.as_string())
+            sys.stdout.write(msg.as_string())
         else:
             sys.stdout.write(fp.read())
 
diff -pru mailman-cvs-2.1-maint/bin/update mailman-2.1.7.cg/bin/update
--- mailman-cvs-2.1-maint/bin/update	2005-08-27 03:40:16.000000000 +0200
+++ mailman-2.1.7.cg/bin/update	2006-03-06 12:12:27.000000000 +0100
@@ -344,6 +344,7 @@ script.
     #
     tmpl_dir = os.path.join(mm_cfg.PREFIX, "templates")
     list_dir = os.path.join(mm_cfg.PREFIX, "lists")
+    # NDIM XXX internal_name
     b4_tmpl_dir = os.path.join(tmpl_dir, mlist._internal_name)
     new_tmpl_dir = os.path.join(list_dir, mlist._internal_name)
     if os.path.exists(b4_tmpl_dir):
diff -pru mailman-cvs-2.1-maint/contrib/mmdsr mailman-2.1.7.cg/contrib/mmdsr
--- mailman-cvs-2.1-maint/contrib/mmdsr	2006-01-29 18:40:36.000000000 +0100
+++ mailman-2.1.7.cg/contrib/mmdsr	2005-12-25 04:21:56.000000000 +0100
@@ -112,43 +112,6 @@
 #		Adrian discovered that there was a log file format
 #		change between Mailman 2.1.5 and 2.1.6, which broke
 #		hourly statistics.
-#
-# 0.0.13	Update by Brad Knowles <brad@stop.mail-abuse.org>
-#		Updated on: Mon Dec 26 05:54:27 CET 2005
-#		Bugs found by Tom G. Christensen (tgc99):
-#		> ps output on solaris is full of whitespace but a further
-#		> echo get's rid of it.
-#		> The lines in the smtp log are sometimes broken up by a
-#		> newline (right before the msgid) which throws of the
-#		> summary. Piping it through sed first will rejoin the broken
-#		> lines.
-#		> Use $AWK instead of awk.
-#		Enhancements from Mark Sapiro:
-#		> The vette log summary lists posts held for moderation
-#		> individually under "Other Errors:". The following patch
-#		> (watch out for wrapped lines) summarizes them by list instead.
-#
-# 0.0.14	Update by Brad Knowles <brad@stop.mail-abuse.org>
-#		Updated on: Thu Dec 29 08:17:38 CET 2005
-#		Added code to check /usr/local/mailman/data for
-#		moderation hold queue
-#
-# 0.0.15	Update by Brad Knowles <brad@stop.mail-abuse.org>
-#		Updated on: Thu Jan 26 02:39:38 CET 2006
-#		Tweaked display of summary data in the "smtp-failure" log,
-#		and "other" category in the "vette" log, so as to reduce
-#		the spewage when things go wonky, mostly by removing
-#		unique-ifying data like message-id or sender address.
-#		Also tweaked moderation queue information, to tell us
-#		how many Python pickle files are in the data directory,
-#		as opposed to just doing a directory listing and skipping
-#		the files in the middle if there are too many -- now we
-#		know how many are being skipped.
-#
-# 0.0.16	Update by Brad Knowles <brad@stop.mail-abuse.org>
-#		Updated on: Sun Jan 29 11:45:58 CET 2006
-#		Mark found a couple of typos that I somehow let slip
-#		through.  Thanks!
 
 ###############################################################################
 # Set up locations of standard commands, directories, etc....
@@ -193,19 +156,9 @@ QUEUEDIR="/usr/local/mailman/qfiles"
 LOGDIR="/usr/local/mailman/logs"
 
 ###############################################################################
-# Mailman data directory
-###############################################################################
-DATADIR="/usr/local/mailman/data"
-
-###############################################################################
 # Maximum number of subdirectory entries to display in report
 ###############################################################################
-MAX_QUEUE_LINES=20
-
-###############################################################################
-# Maximum number of moderation queue pickle files to display in report
-###############################################################################
-MAX_DATA_LINES=100
+MAX_LINES=20
 
 ###############################################################################
 # Mailman Log files to check for errors.
@@ -229,8 +182,8 @@ SUBDIRS="archive bounces commands in new
 # Specify sender address for report.  Not used if recipient list is empty.
 ###############################################################################
 
-SENDER="INSERT.YOUR.SENDER.ADDRESS@HERE"
-RCPTS="INSERT.YOUR.RECIPIENTS.ADDRESSES@HERE"
+SENDER="put@your.address.here"
+RCPTS="put@the.address.of,your@recipients.here"
 
 ###############################################################################
 # If you run this program in cron at 23:59:00, you need to sleep sixty
@@ -250,8 +203,7 @@ SLEEPTIME=60
 #	there is an easier cross-platform way to do it, please let me know.
 ###############################################################################
 
-GRABUID=`$PS -o user -p $$ | $TAIL -1`
-MYUID=`echo $GRABUID`
+MYUID=`$PS -o user -p $$ | $TAIL -1`
 RUNAS="mailman"
 
 ###############################################################################
@@ -302,9 +254,9 @@ do
     $TOUCH $TMPLOG
     echo "Log file: $LOG" >> $TMP
     echo "==============================" >> $TMP
+    $GREP -si "^$DAY [0-9][0-9:]* $YEAR" $LOGDIR/$LOG >> $TMPLOG
 
     if [ -f "$LOGDIR/${LOG}" ] ; then
-    $SED -e :a -e '$!N;s/\n //;ta' -e 'P;D' $LOGDIR/$LOG | $GREP -si "^$DAY [0-9][0-9:]* $YEAR" >> $TMPLOG
 
 	if [ "${LOG}" = "post" ] ; then
 
@@ -312,7 +264,7 @@ do
 	    echo "Hourly Summary of Posts" >> $TMP
 	    echo "-----------------------" >> $TMP
 
-	    $SED -e 's/^[A-Z][a-z][a-z] *[0-9]* //' -e 's/:.*$//' $TMPLOG | $UNIQ -c | $SORT -n +1 | $AWK '{ printf( "%8d  %02d:00-%02d:59\n", $1, $2, $2 ) }' >> $TMP
+	    $SED -e 's/^[A-Z][a-z][a-z] *[0-9]* //' -e 's/:.*$//' $TMPLOG | $UNIQ -c | $SORT -n +1 | awk '{ printf( "%8d  %02d:00-%02d:59\n", $1, $2, $2 ) }' >> $TMP
 
 	    echo "" >> $TMP
 	    echo "Post Count by List" >> $TMP
@@ -343,7 +295,7 @@ do
 	    echo "" >> $TMP
 	    echo "Hourly Summary of Messages Sent" >> $TMP
 	    echo "-------------------------------" >> $TMP
-	    $SED -e 's/^[A-Z][a-z][a-z] *[0-9]* //' -e 's/:.* for / /' -e 's/ recips,.*$//' $TMPLOG | $AWK '{ val=int($1); sum[val]+=$2 } END { for (i=0; i<24; i++) { printf "%8d %02d:00-%02d:59\n", sum[i], i, i } }' >> $TMP
+	    $SED -e 's/^[A-Z][a-z][a-z] *[0-9]* //' -e 's/:.* for / /' -e 's/ recips,.*$//' $TMPLOG | awk '{ val=int($1); sum[val]+=$2 } END { for (i=0; i<24; i++) { printf "%8d %02d:00-%02d:59\n", sum[i], i, i } }' >> $TMP
 
 	else
 
@@ -464,18 +416,9 @@ do
 	    $GREP -i 'Posting to a moderated newsgroup' $TMPLOG | $AWK '{ print $6 }' | $SORT | $UNIQ -c | $SORT -nr >> $TMP
 
 	    echo "" >> $TMP
-	    echo "Post to moderated list (by list):" >> $TMP
-	    echo "------------------------------" >> $TMP
-	    $GREP -i 'Post to moderated list' $TMPLOG | $AWK '{ print $6 }' | $SORT | $UNIQ -c | $SORT -nr >> $TMP
-
-	    echo "" >> $TMP
 	    echo "Other Errors:" >> $TMP
 	    echo "------------------------------" >> $TMP
-	    $EGREP -vi '(Post by non-member|suspicious header|message approved|Discarded posting|bulk message discarded|junk message discarded|Message has implicit destination|Posting to a moderated newsgroup|Post to moderated list|Message discarded, msgid)' $TMPLOG | $SED -e 's/^.* ([0-9]*) //' -e 's/, message-id=<[^> ]*>:/:/' | $SORT | $UNIQ -c | $SORT -nr >> $TMP 
-
-	elif [ "${LOG}" = "smtp-failure" ] ; then
-
-	    $SED 's/^.* ([0-9]*) //' $TMPLOG | $SED 's/delivery to [^@ ]*@[^@ ]* failed with code/delivery failed with code/g' | $SORT | $UNIQ -c | $SORT -nr >> $TMP 
+	    $EGREP -vi '(Post by non-member|suspicious header|message approved|Discarded posting|bulk message discarded|junk message discarded|Message has implicit destination|Posting to a moderated newsgroup|Message discarded, msgid)' $TMPLOG | $SED 's/^.* ([0-9]*) //' | $SORT | $UNIQ -c | $SORT -nr >> $TMP 
 
 	else
 
@@ -503,7 +446,7 @@ do
     $RM -f $TMPLOG
     $TOUCH $TMPLOG
     $LS -la $DIR >> $TMPLOG
-    LINES=`$WC -l < $TMPLOG`
+    LINES=`wc -l < $TMPLOG`
     LINES=`expr $LINES - 3`
 
     echo "Subdirectory: $DIR" >> $TMP
@@ -511,12 +454,12 @@ do
     echo "Entries: $LINES" >> $TMP
     echo "------------------------------" >> $TMP
 
-    if [ $LINES -le $MAX_QUEUE_LINES ] ; then
+    if [ $LINES -le $MAX_LINES ] ; then
     	$CAT $TMPLOG >> $TMP
     else
     	$HEAD $TMPLOG >> $TMP
 	echo "" >> $TMP
-	echo "	More than $MAX_QUEUE_LINES total entries, skipping ..." >> $TMP
+	echo "	More than $MAX_LINES total entries, skipping ..." >> $TMP
 	echo "" >> $TMP
     	$TAIL $TMPLOG >> $TMP
     fi
@@ -524,45 +467,6 @@ do
 
 done
 
-echo "******************************" >> $TMP
-echo "Data Directory Contents" >> $TMP
-echo "	(Python pickle files only)" >> $TMP
-echo "******************************" >> $TMP
-
-cd $DATADIR
-
-$RM -f $TMPLOG
-$TOUCH $TMPLOG
-$LS -la | $GREP -i '\.pck$' >> $TMPLOG
-LINES=`$WC -l < $TMPLOG`
-
-echo "Python pickle files: $LINES" >> $TMP
-echo "==============================" >> $TMP
-echo "" >> $TMP
-
-if [ $LINES -le $MAX_DATA_LINES ] ; then
-    $CAT $TMPLOG >> $TMP
-else
-    $HEAD $TMPLOG >> $TMP
-    echo "" >> $TMP
-    echo "	More than $MAX_DATA_LINES total entries, skipping ..." >> $TMP
-    echo "" >> $TMP
-    $TAIL $TMPLOG >> $TMP
-fi
-echo "" >> $TMP
-
-echo "******************************" >> $TMP
-echo "Moderation Queue Summary" >> $TMP
-echo "******************************" >> $TMP
-echo "" >> $TMP
-
-$AWK '{ print $NF }' $TMPLOG | $GREP -i '^heldmsg-' | $SED -e 's/^heldmsg-//' -e 's/-[0-9]*\.pck$//' | $SORT | $UNIQ -c | $SORT -nr >> $TMP
-echo "" >> $TMP
-
-###############################################################################
-# Okay, we're done with all the code stuff, now handle the output
-###############################################################################
-
 if [ "${RCPTS}x" != "x" ] ; then
     $SENDMAIL -t -f$SENDER < $TMP
 else
diff -pru mailman-cvs-2.1-maint/contrib/README.mmdsr mailman-2.1.7.cg/contrib/README.mmdsr
--- mailman-cvs-2.1-maint/contrib/README.mmdsr	2006-01-29 13:55:26.000000000 +0100
+++ mailman-2.1.7.cg/contrib/README.mmdsr	2005-12-30 19:58:15.000000000 +0100
@@ -5,41 +5,31 @@ for mailman.  It was initially posted at
 <http://sourceforge.net/tracker/index.php?func=detail&aid=1123383&group_id=103&atid=300103>
 which see for possible patches and other enhancements.
 
-Here goes the original mmdsr.readme by Brad ...
+It is intended that there will be a 'cleaner', more complete readme file in
+the future.  In the interim, here are Brad's original comments.
 
-========================================================================
-This is a basic Bourne shell script that I quickly hacked together for
-my own purposes, designed to be fired off at 23:59 every night, going
-through a variety of Mailman log files looking for entries specific
-to that date, summarizing the activities, and indicating problems or
-certain types of activity that might be of interest to someone trying
-to administer the server.
-
-It also does an "ls -la" of /usr/local/mailman/qfiles/*, so that you
-can see what is in the queue at the time of the execution of the script.
-
-This daily report will get e-mailed to the admin, or posted to a "reports"
-mailing list, where they can be archived and kept for future reference.
-If you don't define an address where the output e-mail should be sent,
-it will instead be printed to the standard output (thus allowing you to
-do something else with it).
-
-
-Once I'd gone through a few revisions of my own on this tool, I
-thought that I would release the code to the public and get comments
-and suggestions from others in the Mailman community.  This program is
-currently being used actively on the mail servers for python.org (where
-the mailman-users and other Mailman-related mailing lists are hosted),
-as well as many others.
-
-Note that this script needs to be configured once to know where standard
-commands are located, where log files are kept, etc... (see the top
-500 lines or so of the script), but after that you don't need to feed
-it any input, or capture the output to be sent anywhere.  This script
-takes care of all of that.  All you should need to do is to call this
-script from a cron job at 23:59 (local time) every night.
-
-
-When looking at this script, perhaps during configuration, please keep
-in mind that it is heavily commented at the top, and everything should
-hopefully be self-evident.
+I quickly whacked together a Daily Status Report script for Mailman (using
+Bourne shell, not Python ;), and thought that other folks might be interested
+in seeing it.
+
+The basic concept is a program that gets fired off at 23:59 every night, and
+goes through a variety of log files looking for entries specific to that date,
+and indicating problems or certain types of activity that might be of interest
+to someone trying to administer the server.  It also does an "ls -la" of
+/usr/local/mailman/qfiles/*, so that you can see what is in the queue at the
+time of the running of the script.
+
+My concept was that this daily report would get e-mailed to the admin, or
+posted to a "reports" mailing list, where they could be archived and kept for
+future reference.
+
+The script does not (yet) do any statistics calculations, although it should
+be relatively easy to hack together some basic stats using awk, sort, etc....
+
+Anyway, I thought I'd share it and let folks take a look at it, and if anyone
+has any recommended improvements, we can incorporate those and share them back
+out with everyone.
+
+The code is written under a BSD-style license, so if you don't want to
+contribute any changes back to me, that's okay. Of course, I would prefer that
+you did, but I leave the choice up to you.
diff -pru mailman-cvs-2.1-maint/Mailman/Archiver/Archiver.py mailman-2.1.7.cg/Mailman/Archiver/Archiver.py
--- mailman-cvs-2.1-maint/Mailman/Archiver/Archiver.py	2005-08-27 03:40:15.000000000 +0200
+++ mailman-2.1.7.cg/Mailman/Archiver/Archiver.py	2006-03-08 18:05:58.000000000 +0100
@@ -24,7 +24,6 @@ archival.
 
 import os
 import errno
-import traceback
 import re
 from cStringIO import StringIO
 
@@ -46,6 +45,17 @@ except NameError:
 
 def makelink(old, new):
     try:
+        # Before symlinking, make sure that the directory where the
+        # symlink will be created exists.
+        omask = os.umask(0)
+        try:
+            try:
+                os.makedirs(os.path.split(new)[0], 02775)
+            except OSError, e:
+                if e.errno <> errno.EEXIST:
+                    raise
+        finally:
+            os.umask(omask)
         os.symlink(old, new)
     except OSError, e:
         if e.errno <> errno.EEXIST:
@@ -80,9 +90,17 @@ class Archiver:
         #             listname.mbox
         #         listname/
         #             lots-of-pipermail-stuff
+        #         domain.com/
+        #             listname.mbox/
+        #                 listname.mbox
+        #             listname/
+        #                 lots-of-pipermail-stuff
         #     public/
         #         listname.mbox@ -> ../private/listname.mbox
         #         listname@ -> ../private/listname
+        #         domain.com/
+        #             listname.mbox@ -> ../../private/listname.mbox
+        #             listname@ -> ../../private/listname
         #
         # IOW, the mbox and pipermail archives are always stored in the
         # private archive for the list.  This is safe because archives/private
@@ -93,7 +111,7 @@ class Archiver:
         omask = os.umask(0)
         try:
             try:
-                os.mkdir(self.archive_dir()+'.mbox', 02775)
+                os.makedirs(self.archive_dir()+'.mbox', 02775)
             except OSError, e:
                 if e.errno <> errno.EEXIST: raise
                 # We also create an empty pipermail archive directory into
@@ -101,7 +119,7 @@ class Archiver:
                 # that lists that have not yet received a posting have
                 # /something/ as their index.html, and don't just get a 404.
             try:
-                os.mkdir(self.archive_dir(), 02775)
+                os.makedirs(self.archive_dir(), 02775)
             except OSError, e:
                 if e.errno <> errno.EEXIST: raise
             # See if there's an index.html file there already and if not,
@@ -132,8 +150,7 @@ class Archiver:
 
     def ArchiveFileName(self):
         """The mbox name where messages are left for archive construction."""
-        return os.path.join(self.archive_dir() + '.mbox',
-                            self.internal_name() + '.mbox')
+        return Site.get_mboxpath(self.internal_name())
 
     def GetBaseArchiveURL(self):
         url = self.GetScriptURL('private', absolute=1) + '/'
@@ -143,7 +160,7 @@ class Archiver:
             hostname = re.match('[^:]*://([^/]*)/.*', url).group(1)\
                        or mm_cfg.DEFAULT_URL_HOST
             url = mm_cfg.PUBLIC_ARCHIVE_URL % {
-                'listname': self.internal_name(),
+                'listname': self.local_part,
                 'hostname': hostname
                 }
             if not url.endswith('/'):
diff -pru mailman-cvs-2.1-maint/Mailman/Archiver/HyperArch.py mailman-2.1.7.cg/Mailman/Archiver/HyperArch.py
--- mailman-cvs-2.1-maint/Mailman/Archiver/HyperArch.py	2006-01-29 06:12:26.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Archiver/HyperArch.py	2006-03-10 10:36:33.000000000 +0100
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2005 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
@@ -52,6 +52,7 @@ from Mailman import i18n
 from Mailman.SafeDict import SafeDict
 from Mailman.Logging.Syslog import syslog
 from Mailman.Mailbox import ArchiverMailbox
+from Mailman import Site
 
 # Set up i18n.  Assume the current language has already been set in the caller.
 _ = i18n._
@@ -304,7 +305,12 @@ class Article(pipermail.Article):
             if charset[0]=="'" and charset[-1]=="'":
                 charset = charset[1:-1]
             try:
-                body = message.get_payload(decode=True)
+                # Check Scrubber-munged payload
+                if message.get('x-mailman-scrubbed'):
+                    decode = False
+                else:
+                    decode = True
+                body = message.get_payload(decode=decode)
             except binascii.Error:
                 body = None
             if body and charset != Utils.GetCharSet(self._lang):
@@ -748,11 +754,11 @@ class HyperArchive(pipermail.T):
 
     def html_TOC(self):
         mlist = self.maillist
-        listname = mlist.internal_name()
-        mbox = os.path.join(mlist.archive_dir()+'.mbox', listname+'.mbox')
+        local_part = mlist.local_part
+        mbox = Site.get_mboxpath(mlist.internal_name())
         d = {"listname": mlist.real_name,
              "listinfo": mlist.GetScriptURL('listinfo', absolute=1),
-             "fullarch": '../%s.mbox/%s.mbox' % (listname, listname),
+             "fullarch": '../%s.mbox/%s.mbox' % (local_part, local_part), # NDIM XXX Path
              "size": sizeof(mbox, mlist.preferred_language),
              'meta': '',
              }
@@ -830,7 +836,7 @@ class HyperArchive(pipermail.T):
             return 1
         self._lock_file = LockFile.LockFile(
             os.path.join(mm_cfg.LOCK_DIR,
-                         self.maillist.internal_name() + '-arch.lock'))
+                         self.maillist.internal_name() + '-arch.lock')) # NDIM XXX Lock
         try:
             self._lock_file.lock(timeout=0.5)
         except LockFile.TimeOutError:
diff -pru mailman-cvs-2.1-maint/Mailman/Archiver/pipermail.py mailman-2.1.7.cg/Mailman/Archiver/pipermail.py
--- mailman-cvs-2.1-maint/Mailman/Archiver/pipermail.py	2006-01-29 06:12:26.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Archiver/pipermail.py	2005-12-26 02:22:08.000000000 +0100
@@ -224,7 +224,12 @@ class Article:
                 self.headers[i] = message[i]
 
         # Read the message body
-        s = StringIO(message.get_payload(decode=True)\
+        # Check Scrubber-munged paylaod
+        if message.get('x-mailman-scrubbed'):
+            decode = False
+        else:
+            decode = True
+        s = StringIO(message.get_payload(decode=decode)\
                      or message.as_string().split('\n\n',1)[1])
         self.body = s.readlines()
 
diff -pru mailman-cvs-2.1-maint/Mailman/Bouncers/BouncerAPI.py mailman-2.1.7.cg/Mailman/Bouncers/BouncerAPI.py
--- mailman-cvs-2.1-maint/Mailman/Bouncers/BouncerAPI.py	2006-03-01 03:34:10.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Bouncers/BouncerAPI.py	2005-08-27 03:40:15.000000000 +0200
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001,2002 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
@@ -12,8 +12,7 @@
 #
 # 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.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
 """Contains all the common functionality for msg bounce scanning API.
 
@@ -63,7 +62,10 @@ def ScanMessages(mlist, msg):
         modname = 'Mailman.Bouncers.' + module
         __import__(modname)
         addrs = sys.modules[modname].process(msg)
-        if addrs:
-            # Return addrs even if it is Stop. BounceRunner needs this info.
+        if addrs is Stop:
+            # One of the detectors recognized the bounce, but there were no
+            # addresses to extract.  Return the empty list.
+            return []
+        elif addrs:
             return addrs
     return []
diff -pru mailman-cvs-2.1-maint/Mailman/Bouncers/DSN.py mailman-2.1.7.cg/Mailman/Bouncers/DSN.py
--- mailman-cvs-2.1-maint/Mailman/Bouncers/DSN.py	2006-03-01 03:34:10.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Bouncers/DSN.py	2005-08-27 03:40:15.000000000 +0200
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2003 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
@@ -12,8 +12,7 @@
 #
 # 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.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
 """Parse RFC 3464 (i.e. DSN) bounce formats.
 
@@ -53,10 +52,9 @@ def check(msg):
             #
             # Also grok out Action so we can do something with that too.
             action = msgblock.get('action', '').lower()
-            # Some MTAs have been observed that put comments on the action.
-            if action.startswith('delayed'):
+            if action == 'delayed':
                 return Stop
-            if not action.startswith('fail'):
+            if action not in ('failed', 'failure'):
                 # Some non-permanent failure, so ignore this block
                 continue
             params = []
diff -pru mailman-cvs-2.1-maint/Mailman/Bouncers/Qmail.py mailman-2.1.7.cg/Mailman/Bouncers/Qmail.py
--- mailman-cvs-2.1-maint/Mailman/Bouncers/Qmail.py	2006-03-01 03:34:10.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Bouncers/Qmail.py	2005-08-27 03:40:15.000000000 +0200
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001,2002 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
@@ -12,8 +12,7 @@
 # 
 # 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.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
 """Parse bounce messages generated by qmail.
 
@@ -29,11 +28,7 @@ This module should be conformant.
 import re
 import email.Iterators
 
-# Other (non-standard?) intros have been observed in the wild.
-introtags = [
-    'Hi. This is the',
-    "We're sorry. There's a problem"
-    ]
+introtag = 'Hi. This is the'
 acre = re.compile(r'<(?P<addr>[^>]*)>:')
 
 
@@ -47,11 +42,8 @@ def process(msg):
     state = 0
     for line in email.Iterators.body_line_iterator(msg):
         line = line.strip()
-        if state == 0:
-            for introtag in introtags:
-                if line.startswith(introtag):
-                    state = 1
-                    break
+        if state == 0 and line.startswith(introtag):
+            state = 1
         elif state == 1 and not line:
             # Looking for the end of the intro paragraph
             state = 2
diff -pru mailman-cvs-2.1-maint/Mailman/Bouncers/SimpleMatch.py mailman-2.1.7.cg/Mailman/Bouncers/SimpleMatch.py
--- mailman-cvs-2.1-maint/Mailman/Bouncers/SimpleMatch.py	2006-03-10 11:36:50.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Bouncers/SimpleMatch.py	2006-02-21 07:21:11.000000000 +0100
@@ -78,31 +78,7 @@ PATTERNS = [
     # Prodigy.net full mailbox
     (_c("User's mailbox is full:"),
      _c('Unable to deliver mail.'),
-     _c("User's mailbox is full:\s*<(?P<addr>[^>]*)>")),
-    # Microsoft SMTPSVC
-    (_c('The email below could not be delivered to the following user:'),
-     _c('Old message:'),
-     _c('<(?P<addr>[^>]*)>')),
-    # Yahoo on behalf of other domains like sbcglobal.net
-    (_c('Unable to deliver message to the following address\(es\)\.'),
-     _c('--- Original message follows\.'),
-     _c('<(?P<addr>[^>]*)>:')),
-    # kundenserver.de
-    (_c('A message that you sent could not be delivered'),
-     _c('^--- The header of the original'),
-     _c('<(?P<addr>[^>]*)>')),
-    # another kundenserver.de
-    (_c('A message that you sent could not be delivered'),
-     _c('^--- The header of the original'),
-     _c('^(?P<addr>[^\s@]+@[^\s@:]+):')),
-    # thehartford.com
-    (_c('Delivery to the following recipients failed'),
-     _c("Bogus - there actually isn't anything"),
-     _c('^\s*(?P<addr>[^\s@]+@[^\s@]+)\s*$')),
-    # and another thehartfod.com/hartfordlife.com
-    (_c('^Your message'),
-     _c('^because:'),
-     _c('^\s*(?P<addr>[^\s@]+@[^\s@]+)\s*$')),
+     _c("User's mailbox is full:\s*<(?P<addr>.*)>.*$")),
     # Next one goes here...
     ]
 
@@ -115,27 +91,19 @@ def process(msg, patterns=None):
     #     0 = nothing seen yet
     #     1 = intro seen
     addrs = {}
-    # MAS: This is a mess. The outer loop used to be over the message
-    # so we only looped through the message once.  Looping through the
-    # message for each set of patterns is obviously way more work, but
-    # if we don't do it, problems arise because scre from the wrong
-    # pattern set matches first and then acre doesn't match.  The
-    # alternative is to split things into separate modules, but then
-    # we process the message multiple times anyway.
-    for scre, ecre, acre in patterns:
-        state = 0
-        for line in email.Iterators.body_line_iterator(msg):
-            if state == 0:
+    state = 0
+    for line in email.Iterators.body_line_iterator(msg):
+        if state == 0:
+            for scre, ecre, acre in patterns:
                 if scre.search(line):
                     state = 1
-            if state == 1:
-                mo = acre.search(line)
-                if mo:
-                    addr = mo.group('addr')
-                    if addr:
-                        addrs[mo.group('addr')] = 1
-                elif ecre.search(line):
                     break
-        if addrs:
-            break
+        if state == 1:
+            mo = acre.search(line)
+            if mo:
+                addr = mo.group('addr')
+                if addr:
+                    addrs[mo.group('addr')] = 1
+            elif ecre.search(line):
+                break
     return addrs.keys()
diff -pru mailman-cvs-2.1-maint/Mailman/Bouncers/SimpleWarning.py mailman-2.1.7.cg/Mailman/Bouncers/SimpleWarning.py
--- mailman-cvs-2.1-maint/Mailman/Bouncers/SimpleWarning.py	2006-03-10 11:36:50.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Bouncers/SimpleWarning.py	2005-08-27 03:40:15.000000000 +0200
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2001,2002 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
@@ -12,12 +12,10 @@
 # 
 # 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.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
 """Recognizes simple heuristically delimited warnings."""
 
-from Mailman.Bouncers.BouncerAPI import Stop
 from Mailman.Bouncers.SimpleMatch import _c
 from Mailman.Bouncers.SimpleMatch import process as _process
 
@@ -43,8 +41,4 @@ patterns = [
 
 
 def process(msg):
-    if _process(msg, patterns):
-        # It's a recognized warning so stop now
-        return Stop
-    else:
-        return []
+    return _process(msg, patterns)
diff -pru mailman-cvs-2.1-maint/Mailman/Bouncers/SMTP32.py mailman-2.1.7.cg/Mailman/Bouncers/SMTP32.py
--- mailman-cvs-2.1-maint/Mailman/Bouncers/SMTP32.py	2006-02-05 20:46:20.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Bouncers/SMTP32.py	2005-08-27 03:40:15.000000000 +0200
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001,2002 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
@@ -12,8 +12,7 @@
 # 
 # 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.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
 """Something which claims
 X-Mailer: <SMTP32 vXXXXXX>
@@ -36,8 +35,7 @@ acre = re.compile(r'''
     (                                             # several different prefixes
     user\ mailbox[^:]*:                           # have been spotted in the
     |delivery\ failed[^:]*:                       # wild...
-    |unknown\ user[^:]*:
-    |undeliverable\ +to
+    |undeliverable\ to
     )
     \s*                                           # space separator
     (?P<addr>.*)                                  # and finally, the address
diff -pru mailman-cvs-2.1-maint/Mailman/Cgi/admindb.py mailman-2.1.7.cg/Mailman/Cgi/admindb.py
--- mailman-cvs-2.1-maint/Mailman/Cgi/admindb.py	2005-12-30 19:50:07.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Cgi/admindb.py	2006-03-10 16:07:26.000000000 +0100
@@ -81,7 +81,7 @@ def main():
         handle_no_list()
         return
 
-    listname = parts[0].lower()
+    listname = Utils.GetListName(parts)
     try:
         mlist = MailList.MailList(listname, lock=0)
     except Errors.MMListError, e:
diff -pru mailman-cvs-2.1-maint/Mailman/Cgi/admin.py mailman-2.1.7.cg/Mailman/Cgi/admin.py
--- mailman-cvs-2.1-maint/Mailman/Cgi/admin.py	2005-12-30 19:50:07.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Cgi/admin.py	2006-03-10 16:07:19.000000000 +0100
@@ -65,8 +65,9 @@ def main():
         # None, so just do the admin overview and be done with it
         admin_overview()
         return
+
     # Get the list object
-    listname = parts[0].lower()
+    listname = Utils.GetListName(parts)
     try:
         mlist = MailList.MailList(listname, lock=0)
     except Errors.MMListError, e:
@@ -418,7 +419,7 @@ def show_results(mlist, doc, category, s
                        '<br>&nbsp;<br>')
     # We do not allow through-the-web deletion of the site list!
     if mm_cfg.OWNERS_CAN_DELETE_THEIR_OWN_LISTS and \
-           mlist.internal_name() <> mm_cfg.MAILMAN_SITE_LIST:
+           mlist.local_name <> mm_cfg.MAILMAN_SITE_LIST:
         otherlinks.AddItem(Link(mlist.GetScriptURL('rmlist'),
                                 _('Delete this mailing list')).Format() +
                            _(' (requires confirmation)<br>&nbsp;<br>'))
diff -pru mailman-cvs-2.1-maint/Mailman/Cgi/confirm.py mailman-2.1.7.cg/Mailman/Cgi/confirm.py
--- mailman-cvs-2.1-maint/Mailman/Cgi/confirm.py	2005-12-30 19:50:07.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Cgi/confirm.py	2006-03-10 16:07:37.000000000 +0100
@@ -53,7 +53,7 @@ def main():
         print doc.Format()
         return
 
-    listname = parts[0].lower()
+    listname = Utils.GetListName(parts)
     try:
         mlist = MailList.MailList(listname, lock=0)
     except Errors.MMListError, e:
diff -pru mailman-cvs-2.1-maint/Mailman/Cgi/edithtml.py mailman-2.1.7.cg/Mailman/Cgi/edithtml.py
--- mailman-cvs-2.1-maint/Mailman/Cgi/edithtml.py	2006-01-09 08:06:52.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Cgi/edithtml.py	2006-03-10 16:07:53.000000000 +0100
@@ -61,7 +61,7 @@ def main():
         print doc.Format()
         return
 
-    listname = parts[0].lower()
+    listname = Utils.GetListName(parts)
     try:
         mlist = MailList.MailList(listname, lock=0)
     except Errors.MMListError, e:
diff -pru mailman-cvs-2.1-maint/Mailman/Cgi/listinfo.py mailman-2.1.7.cg/Mailman/Cgi/listinfo.py
--- mailman-cvs-2.1-maint/Mailman/Cgi/listinfo.py	2005-08-27 03:40:15.000000000 +0200
+++ mailman-2.1.7.cg/Mailman/Cgi/listinfo.py	2006-03-10 16:08:07.000000000 +0100
@@ -42,7 +42,7 @@ def main():
         listinfo_overview()
         return
 
-    listname = parts[0].lower()
+    listname = Utils.GetListName(parts)
     try:
         mlist = MailList.MailList(listname, lock=0)
     except Errors.MMListError, e:
diff -pru mailman-cvs-2.1-maint/Mailman/Cgi/options.py mailman-2.1.7.cg/Mailman/Cgi/options.py
--- mailman-cvs-2.1-maint/Mailman/Cgi/options.py	2005-12-03 02:07:13.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Cgi/options.py	2006-03-10 16:08:20.000000000 +0100
@@ -65,7 +65,7 @@ def main():
         return
 
     # get the list and user's name
-    listname = parts[0].lower()
+    listname = Utils.GetListName(parts)
     # open list
     try:
         mlist = MailList.MailList(listname, lock=0)
diff -pru mailman-cvs-2.1-maint/Mailman/Cgi/private.py mailman-2.1.7.cg/Mailman/Cgi/private.py
--- mailman-cvs-2.1-maint/Mailman/Cgi/private.py	2005-12-30 19:50:07.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Cgi/private.py	2006-04-16 02:20:50.000000000 +0200
@@ -29,6 +29,7 @@ from Mailman import Errors
 from Mailman import i18n
 from Mailman.htmlformat import *
 from Mailman.Logging.Syslog import syslog
+from Mailman.Site import get_mboxpath
 
 # Set up i18n.  Until we know which list is being requested, we use the
 # server's default.
@@ -79,7 +80,7 @@ def main():
     true_filename = os.path.join(
         mm_cfg.PRIVATE_ARCHIVE_FILE_DIR, tpath)
 
-    listname = parts[0].lower()
+    listname = parts[0].lower() # NDIM XXX use Utils.GetListname
     mboxfile = ''
     if len(parts) > 1:
         mboxfile = parts[1]
@@ -166,8 +167,7 @@ def main():
         if ctype is None:
             ctype = 'text/html'
         if mboxfile:
-            f = open(os.path.join(mlist.archive_dir() + '.mbox',
-                                  mlist.internal_name() + '.mbox'))
+            f = open(get_mboxpath(mlist.internal_name()))
             ctype = 'text/plain'
         elif true_filename.endswith('.gz'):
             import gzip
diff -pru mailman-cvs-2.1-maint/Mailman/Cgi/rmlist.py mailman-2.1.7.cg/Mailman/Cgi/rmlist.py
--- mailman-cvs-2.1-maint/Mailman/Cgi/rmlist.py	2005-08-27 03:40:15.000000000 +0200
+++ mailman-2.1.7.cg/Mailman/Cgi/rmlist.py	2006-03-10 16:09:01.000000000 +0100
@@ -55,7 +55,7 @@ def main():
         syslog('error', 'Bad URL specification: %s', parts)
         return
         
-    listname = parts[0].lower()
+    listname = Utils.GetListName(parts)
     try:
         mlist = MailList.MailList(listname, lock=0)
     except Errors.MMListError, e:
@@ -136,8 +136,9 @@ def process_request(doc, cgidata, mlist)
 
     problems = 0
     listname = mlist.internal_name()
+    local_part = mlist.local_part
     for dirtmpl in REMOVABLES:
-        dir = os.path.join(mm_cfg.VAR_PREFIX, dirtmpl % listname)
+        dir = os.path.join(mm_cfg.VAR_PREFIX, dirtmpl % local_part) # NDIM XXX
         if os.path.islink(dir):
             try:
                 os.unlink(dir)
diff -pru mailman-cvs-2.1-maint/Mailman/Cgi/roster.py mailman-2.1.7.cg/Mailman/Cgi/roster.py
--- mailman-cvs-2.1-maint/Mailman/Cgi/roster.py	2005-08-27 03:40:15.000000000 +0200
+++ mailman-2.1.7.cg/Mailman/Cgi/roster.py	2006-03-10 16:09:10.000000000 +0100
@@ -48,7 +48,7 @@ def main():
         error_page(_('Invalid options to CGI script'))
         return
 
-    listname = parts[0].lower()
+    listname = Utils.GetListName(parts)
     try:
         mlist = MailList.MailList(listname, lock=0)
     except Errors.MMListError, e:
diff -pru mailman-cvs-2.1-maint/Mailman/Cgi/subscribe.py mailman-2.1.7.cg/Mailman/Cgi/subscribe.py
--- mailman-cvs-2.1-maint/Mailman/Cgi/subscribe.py	2005-08-27 03:40:15.000000000 +0200
+++ mailman-2.1.7.cg/Mailman/Cgi/subscribe.py	2006-03-10 16:09:20.000000000 +0100
@@ -51,7 +51,7 @@ def main():
         print doc.Format()
         return
 
-    listname = parts[0].lower()
+    listname = Utils.GetListName(parts)
     try:
         mlist = MailList.MailList(listname, lock=0)
     except Errors.MMListError, e:
diff -pru mailman-cvs-2.1-maint/Mailman/Defaults.py.in mailman-2.1.7.cg/Mailman/Defaults.py.in
--- mailman-cvs-2.1-maint/Mailman/Defaults.py.in	2006-01-15 22:01:35.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Defaults.py.in	2006-03-04 18:29:11.000000000 +0100
@@ -1,6 +1,6 @@
 # -*- python -*-
 
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2005 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
@@ -476,7 +476,6 @@ GLOBAL_PIPELINE = [
     'CalcRecips',
     'AvoidDuplicates',
     'Cleanse',
-    'CleanseDKIM',
     'CookHeaders',
     # And now we send the message to the digest mbox file, and to the arch and
     # news queues.  Runners will provide further processing of the message,
@@ -495,7 +494,6 @@ GLOBAL_PIPELINE = [
 OWNER_PIPELINE = [
     'SpamDetect',
     'Replybot',
-    'CleanseDKIM',
     'OwnerRecips',
     'ToOutgoing',
     ]
@@ -830,7 +828,7 @@ DEFAULT_MSG_HEADER = ""
 DEFAULT_MSG_FOOTER = """_______________________________________________
 %(real_name)s mailing list
 %(real_name)s@%(host_name)s
-%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s
+%(web_page_url)slistinfo%(cgiext)s/%(local_part)s
 """
 
 # Where to put subject prefix for 'Re:' messages:
diff -pru mailman-cvs-2.1-maint/Mailman/Deliverer.py mailman-2.1.7.cg/Mailman/Deliverer.py
--- mailman-cvs-2.1-maint/Mailman/Deliverer.py	2005-12-30 19:50:07.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Deliverer.py	2006-03-04 12:31:03.000000000 +0100
@@ -158,9 +158,8 @@ your membership administrative address, 
         # Some one was invited to one list but tried to confirm to a different
         # list.  We inform both list owners of the bogosity, but be careful
         # not to reveal too much information.
-        selfname = self.internal_name()
         syslog('mischief', '%s was invited to %s but confirmed to %s',
-               address, listname, selfname)
+               address, listname, self.internal_name())
         # First send a notice to the attacked list
         msg = Message.OwnerNotification(
             self,
@@ -207,7 +206,7 @@ is required.""")))
         # probe message.
         token = self.pend_new(Pending.PROBE_BOUNCE, member, msg)
         probedict = {
-            'bounces': self.internal_name() + '-bounces',
+            'bounces': self.getListAddress('bounces'),
             'token': token,
             }
         probeaddr = '%s@%s' % ((mm_cfg.VERP_PROBE_FORMAT % probedict),
diff -pru mailman-cvs-2.1-maint/Mailman/Gui/Digest.py mailman-2.1.7.cg/Mailman/Gui/Digest.py
--- mailman-cvs-2.1-maint/Mailman/Gui/Digest.py	2005-08-27 03:40:15.000000000 +0200
+++ mailman-2.1.7.cg/Mailman/Gui/Digest.py	2006-03-04 18:41:06.000000000 +0100
@@ -25,8 +25,9 @@ from Mailman.Gui.GUIBase import GUIBase
 
 # Common b/w nondigest and digest headers & footers.  Personalizations may add
 # to this.
+# NDIM XXX remove _internal_name?
 ALLOWEDS = ('real_name', 'list_name', 'host_name', 'web_page_url',
-            'description', 'info', 'cgiext', '_internal_name',
+            'description', 'info', 'cgiext', '_internal_name', 'local_part',
             )
 
 
diff -pru mailman-cvs-2.1-maint/Mailman/Gui/General.py mailman-2.1.7.cg/Mailman/Gui/General.py
--- mailman-cvs-2.1-maint/Mailman/Gui/General.py	2005-10-23 10:10:22.000000000 +0200
+++ mailman-2.1.7.cg/Mailman/Gui/General.py	2006-02-21 07:44:48.000000000 +0100
@@ -419,7 +419,7 @@ class General(GUIBase):
 
     def _setValue(self, mlist, property, val, doc):
         if property == 'real_name' and \
-               val.lower() <> mlist.internal_name().lower():
+               val.lower() <> mlist.real_name.lower():
             # These values can't differ by other than case
             doc.addError(_("""<b>real_name</b> attribute not
             changed!  It must differ from the list's name by case
diff -pru mailman-cvs-2.1-maint/Mailman/Gui/Topics.py mailman-2.1.7.cg/Mailman/Gui/Topics.py
--- mailman-cvs-2.1-maint/Mailman/Gui/Topics.py	2006-01-15 00:56:58.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Gui/Topics.py	2006-02-21 07:21:11.000000000 +0100
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2003 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
@@ -12,8 +12,7 @@
 #
 # 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.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
 import re
 
diff -pru mailman-cvs-2.1-maint/Mailman/Handlers/Cleanse.py mailman-2.1.7.cg/Mailman/Handlers/Cleanse.py
--- mailman-cvs-2.1-maint/Mailman/Handlers/Cleanse.py	2006-01-15 22:00:51.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Handlers/Cleanse.py	2005-12-30 19:50:08.000000000 +0100
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2005 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
@@ -50,3 +50,12 @@ def process(mlist, msg, msgdata):
     del msg['x-confirm-reading-to']
     # Pegasus mail uses this one... sigh
     del msg['x-pmrqc']
+    # Remove any "DomainKeys" (or similar) header lines.  The values contained
+    # in these header lines are intended to be used by the recipient to detect
+    # forgery or tampering in transit, and the modifications made by Mailman
+    # to the headers and body of the message will cause these keys to appear
+    # invalid.  Removing them will at least avoid this misleading result, and
+    # it will also give the MTA the opportunity to regenerate valid keys
+    # originating at the Mailman server for the outgoing message.
+    del msg['domainkey-signature']
+    del msg['dkim-signature']
diff -pru mailman-cvs-2.1-maint/Mailman/Handlers/CookHeaders.py mailman-2.1.7.cg/Mailman/Handlers/CookHeaders.py
--- mailman-cvs-2.1-maint/Mailman/Handlers/CookHeaders.py	2006-02-17 03:42:35.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Handlers/CookHeaders.py	2006-03-04 08:59:18.000000000 +0100
@@ -180,7 +180,7 @@ def process(mlist, msg, msgdata):
     if msgdata.get('_nolist') or not mlist.include_rfc2369_headers:
         return
     # This will act like an email address for purposes of formataddr()
-    listid = '%s.%s' % (mlist.internal_name(), mlist.host_name)
+    listid = '%s.%s' % (mlist.local_part, mlist.host_name)
     cset = Utils.GetCharSet(mlist.preferred_language)
     if mlist.description:
         # Don't wrap the header since here we just want to get it properly RFC
@@ -327,16 +327,20 @@ def prefix_subject(mlist, msg, msgdata):
 
 
 
-def ch_oneline(headerstr):
+def ch_oneline(s):
     # Decode header string in one line and convert into single charset
     # copied and modified from ToDigest.py and Utils.py
     # return (string, cset) tuple as check for failure
     try:
-        d = decode_header(headerstr)
+        d = decode_header(s)
         # at this point, we should rstrip() every string because some
         # MUA deliberately add trailing spaces when composing return
         # message.
-        d = [(s.rstrip(), c) for (s,c) in d]
+        i = 0
+        for (s,c) in d:
+            s = s.rstrip()
+            d[i] = (s,c)
+            i += 1
         cset = 'us-ascii'
         for x in d:
             # search for no-None charset
@@ -349,4 +353,4 @@ def ch_oneline(headerstr):
         return oneline.encode(cset, 'replace'), cset
     except (LookupError, UnicodeError, ValueError, HeaderParseError):
         # possibly charset problem. return with undecoded string in one line.
-        return ''.join(headerstr.splitlines()), 'us-ascii'
+        return ''.join(s.splitlines()), 'us-ascii'
diff -pru mailman-cvs-2.1-maint/Mailman/Handlers/Decorate.py mailman-2.1.7.cg/Mailman/Handlers/Decorate.py
--- mailman-cvs-2.1-maint/Mailman/Handlers/Decorate.py	2006-01-29 06:12:26.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Handlers/Decorate.py	2006-03-04 18:54:25.000000000 +0100
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2005 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
@@ -98,7 +98,12 @@ def process(mlist, msg, msgdata):
         uheader = unicode(header, lcset)
         ufooter = unicode(footer, lcset)
         try:
-            oldpayload = unicode(msg.get_payload(decode=True), mcset)
+            # First, check if the message was Scrubber-munged
+            if msg.get('x-mailman-scrubbed'):
+                decode = False
+            else:
+                decode = True
+            oldpayload = unicode(msg.get_payload(decode=decode), mcset)
             frontsep = endsep = u''
             if header and not header.endswith('\n'):
                 frontsep = u'\n'
@@ -193,12 +198,12 @@ def decorate(mlist, template, what, extr
     # example, a footer of "% silly %(real_name)s" would give a header
     # containing all list attributes.  While we've previously removed such
     # really bad ones like `password' and `passwords', it's much better to
-    # provide a whitelist of known good attributes, then to try to remove a
+    # provide a whitelist of known good attributes, than to try to remove a
     # blacklist of known bad ones.
     d = SafeDict({'real_name'     : mlist.real_name,
-                  'list_name'     : mlist.internal_name(),
+                  'list_name'     : mlist.local_part,
                   # For backwards compatibility
-                  '_internal_name': mlist.internal_name(),
+                  '_internal_name': mlist.local_part,
                   'host_name'     : mlist.host_name,
                   'web_page_url'  : mlist.web_page_url,
                   'description'   : mlist.description,
diff -pru mailman-cvs-2.1-maint/Mailman/Handlers/Hold.py mailman-2.1.7.cg/Mailman/Handlers/Hold.py
--- mailman-cvs-2.1-maint/Mailman/Handlers/Hold.py	2005-09-19 05:08:57.000000000 +0200
+++ mailman-2.1.7.cg/Mailman/Handlers/Hold.py	2006-03-04 18:51:57.000000000 +0100
@@ -129,12 +129,13 @@ def process(mlist, msg, msgdata):
         return
     # Get the sender of the message
     listname = mlist.internal_name()
-    adminaddr = listname + '-admin'
+    adminaddr = mlist.getListAddress('admin')
     sender = msg.get_sender()
     # Special case an ugly sendmail feature: If there exists an alias of the
     # form "owner-foo: bar" and sendmail receives mail for address "foo",
     # sendmail will change the envelope sender of the message to "bar" before
     # delivering.  This feature does not appear to be configurable.  *Boggle*.
+    # NDIM XXX Handle listname == 'foo@blah.com'
     if not sender or sender[:len(listname)+6] == adminaddr:
         sender = msg.get_sender(use_envelope=0)
     #
diff -pru mailman-cvs-2.1-maint/Mailman/Handlers/Scrubber.py mailman-2.1.7.cg/Mailman/Handlers/Scrubber.py
--- mailman-cvs-2.1-maint/Mailman/Handlers/Scrubber.py	2006-02-19 22:03:38.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Handlers/Scrubber.py	2005-12-30 19:50:08.000000000 +0100
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2005 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
@@ -111,9 +111,9 @@ class ScrubberGenerator(Generator):
             Generator._write_headers(self, msg)
 
 
-def safe_strftime(fmt, t):
+def safe_strftime(fmt, floatsecs):
     try:
-        return time.strftime(fmt, t)
+        return time.strftime(fmt, floatsecs)
     except (TypeError, ValueError, OverflowError):
         return None
 
@@ -164,10 +164,12 @@ def calculate_attachments_dir(mlist, msg
 
 def replace_payload_by_text(msg, text, charset):
     # TK: This is a common function in replacing the attachment and the main
-    # message by a text (scrubbing).
+    # message by a text (scrubbing).  Also, add a flag indicating it has been
+    # scrubbed.
     del msg['content-type']
     del msg['content-transfer-encoding']
     msg.set_payload(text, charset)
+    msg['X-Mailman-Scrubbed'] = 'Yes'
 
 
 
@@ -342,13 +344,13 @@ Url : %(url)s
                 text.append(_('Skipped content of type %(partctype)s\n'))
                 continue
             try:
-                t = part.get_payload(decode=True)
-            # MAS: TypeError exception can occur if payload is None. This
-            # was observed with a message that contained an attached
-            # message/delivery-status part. Because of the special parsing
-            # of this type, this resulted in a text/plain sub-part with a
-            # null body. See bug 1430236.
-            except (binascii.Error, TypeError):
+                # Check if the part is replaced.
+                if part.get('x-mailman-scrubbed'):
+                    decode = False
+                else:
+                    decode = True
+                t = part.get_payload(decode=decode)
+            except binascii.Error:
                 t = part.get_payload()
             # TK: get_content_charset() returns 'iso-2022-jp' for internally
             # crafted (scrubbed) 'euc-jp' text part. So, first try
diff -pru mailman-cvs-2.1-maint/Mailman/Handlers/SMTPDirect.py mailman-2.1.7.cg/Mailman/Handlers/SMTPDirect.py
--- mailman-cvs-2.1-maint/Mailman/Handlers/SMTPDirect.py	2005-12-30 19:50:08.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Handlers/SMTPDirect.py	2006-03-04 18:54:50.000000000 +0100
@@ -175,7 +175,7 @@ def process(mlist, msg, msgdata):
                           'size'    : len(msg.as_string()),
                           '#recips' : len(recips),
                           '#refused': len(refused),
-                          'listname': mlist.internal_name(),
+                          'listname': mlist.local_part,
                           'sender'  : origsender,
                           })
     # We have to use the copy() method because extended call syntax requires a
diff -pru mailman-cvs-2.1-maint/Mailman/Handlers/SpamDetect.py mailman-2.1.7.cg/Mailman/Handlers/SpamDetect.py
--- mailman-cvs-2.1-maint/Mailman/Handlers/SpamDetect.py	2006-01-29 01:46:05.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Handlers/SpamDetect.py	2006-02-21 07:21:11.000000000 +0100
@@ -102,6 +102,15 @@ def process(mlist, msg, msgdata):
             if mo:
                 # we've detected spam, so throw the message away
                 raise SpamDetected
+    # Before we go to header_filter_rules, we exclude internally generated
+    # owner notification from checking, because 1) we collect headers from
+    # all the attachments but this will cause matching the filter rule again,
+    # and 2) list owners may want to check header name / value pair like
+    # 'Precedence: bulk' which is also generated by mailman.  Both will
+    # cause loop of holding owner notification messages if the action is
+    # set to 'hold'.
+    if msgdata.get('toowner') and msg.get('x-list-administrivia') == 'yes':
+        return
     # Now do header_filter_rules
     # TK: Collect headers in sub-parts because attachment filename
     # extension may be a clue to possible virus/spam.
@@ -123,19 +132,9 @@ def process(mlist, msg, msgdata):
                 if action == mm_cfg.DISCARD:
                     raise Errors.DiscardMessage
                 if action == mm_cfg.REJECT:
-                    if msgdata.get('toowner'):
-                        # Don't send rejection notice if addressed to '-owner'
-                        # because it may trigger a loop of notices if the
-                        # sender address is forged.  We just discard it here.
-                        raise Errors.DiscardMessage
                     raise Errors.RejectMessage(
                         _('Message rejected by filter rule match'))
                 if action == mm_cfg.HOLD:
-                    if msgdata.get('toowner'):
-                        # Don't hold '-owner' addressed message.  We just
-                        # pass it here but list-owner can set this to be
-                        # discarded on the GUI if he wants.
-                        return
                     hold_for_approval(mlist, msg, msgdata, HeaderMatchHold)
                 if action == mm_cfg.ACCEPT:
                     return
diff -pru mailman-cvs-2.1-maint/Mailman/Handlers/ToArchive.py mailman-2.1.7.cg/Mailman/Handlers/ToArchive.py
--- mailman-cvs-2.1-maint/Mailman/Handlers/ToArchive.py	2006-01-29 06:12:26.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Handlers/ToArchive.py	2005-12-30 19:50:08.000000000 +0100
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2005 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
@@ -20,7 +20,10 @@
 import time
 from cStringIO import StringIO
 
+from email import message_from_string
+
 from Mailman import mm_cfg
+from Mailman import Message
 from Mailman.Queue.sbcache import get_switchboard
 
 
@@ -37,4 +40,10 @@ def process(mlist, msg, msgdata):
     # Send the message to the archiver queue
     archq = get_switchboard(mm_cfg.ARCHQUEUE_DIR)
     # Send the message to the queue
-    archq.enqueue(msg, msgdata)
+    if msg.get('x-mailman-scrubbed'):
+        # Clean Scrubber-munged message.
+        archmsg = message_from_string(msg.as_string(), Message.Message)
+        del archmsg['x-mailman-scrubbed']
+        archq.enqueue(archmsg, msgdata)
+    else:
+        archq.enqueue(msg, msgdata)
diff -pru mailman-cvs-2.1-maint/Mailman/Handlers/ToDigest.py mailman-2.1.7.cg/Mailman/Handlers/ToDigest.py
--- mailman-cvs-2.1-maint/Mailman/Handlers/ToDigest.py	2006-01-29 06:12:26.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Handlers/ToDigest.py	2005-12-30 19:50:08.000000000 +0100
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2005 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
@@ -32,6 +32,7 @@ import time
 from types import ListType
 from cStringIO import StringIO
 
+from email import message_from_string
 from email.Parser import Parser
 from email.Generator import Generator
 from email.MIMEBase import MIMEBase
@@ -332,10 +333,14 @@ def send_i18n_digests(mlist, mboxfp):
                 uh = '\n\t'.join(uh.split('\n'))
                 print >> plainmsg, uh
         print >> plainmsg
-        # If decoded payload is empty, this may be multipart message.
-        # -- just stringfy it.
-        payload = msg.get_payload(decode=True) \
-                  or msg.as_string().split('\n\n',1)[1]
+        if msg.get('x-mailman-scrubbed'):
+            # It has successfully been scrubbed, so this should be string.
+            payload = msg.get_payload()
+        else:
+            # If decoded payload is empty, this may be multipart message.
+            # -- just stringfy it.
+            payload = msg.get_payload(decode=True) \
+                      or msg.as_string().split('\n\n',1)[1]
         mcset = msg.get_content_charset('')
         if mcset and mcset <> lcset and mcset <> lcset_out:
             try:
@@ -407,6 +412,8 @@ def send_i18n_digests(mlist, mboxfp):
                     isdigest=True)
     # RFC 1153
     rfc1153msg.set_payload(plainmsg.getvalue(), lcset)
+    # Re-generate it because set_payload() doesn't encode. :-(
+    rfc1153msg = message_from_string(rfc1153msg.as_string(), Message.Message)
     virginq.enqueue(rfc1153msg,
                     recips=plainrecips,
                     listname=mlist.internal_name(),
diff -pru mailman-cvs-2.1-maint/Mailman/HTMLFormatter.py mailman-2.1.7.cg/Mailman/HTMLFormatter.py
--- mailman-cvs-2.1-maint/Mailman/HTMLFormatter.py	2005-08-27 03:40:15.000000000 +0200
+++ mailman-2.1.7.cg/Mailman/HTMLFormatter.py	2006-03-04 18:43:07.000000000 +0100
@@ -381,7 +381,8 @@ class HTMLFormatter:
         d = {
             '<mm-mailman-footer>' : self.GetMailmanFooter(),
             '<mm-list-name>' : self.real_name,
-            '<mm-email-user>' : self._internal_name,
+            '<mm-email-user>' : self.local_part,
+            '<mm-complete-name>' : self.internal_name(),
             '<mm-list-description>' : self.description,
             '<mm-list-info>' : BR.join(self.info.split(NL)),
             '<mm-form-end>'  : self.FormatFormEnd(),
diff -pru mailman-cvs-2.1-maint/Mailman/i18n.py mailman-2.1.7.cg/Mailman/i18n.py
--- mailman-cvs-2.1-maint/Mailman/i18n.py	2006-02-23 07:25:29.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/i18n.py	2005-08-27 03:40:15.000000000 +0200
@@ -1,4 +1,4 @@
-# Copyright (C) 2000-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2000-2003 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
@@ -12,8 +12,7 @@
 #
 # 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.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
 import sys
 import time
@@ -108,12 +107,7 @@ def ctime(date):
     if isinstance(date, StringType):
         try:
             year, mon, day, hh, mm, ss, wday, ydat, dst = time.strptime(date)
-            if dst in (0,1):
-                tzname = time.tzname[dst]
-            else:
-                # MAS: No exception but dst = -1 so try
-                return ctime(time.mktime((year, mon, day, hh, mm, ss, wday,
-                                          ydat, dst)))
+            tzname = time.tzname[dst and 1 or 0]
         except (ValueError, AttributeError):
             try:
                 wday, mon, day, hms, year = date.split()
@@ -138,8 +132,7 @@ def ctime(date):
                         break
     else:
         year, mon, day, hh, mm, ss, wday, yday, dst = time.localtime(date)
-        if dst in (0,1):
-            tzname = time.tzname[dst]
+        tzname = time.tzname[dst and 1 or 0]
 
     wday = daysofweek[wday]
     mon = months[mon]
diff -pru mailman-cvs-2.1-maint/Mailman/ListAdmin.py mailman-2.1.7.cg/Mailman/ListAdmin.py
--- mailman-cvs-2.1-maint/Mailman/ListAdmin.py	2005-08-27 03:40:15.000000000 +0200
+++ mailman-2.1.7.cg/Mailman/ListAdmin.py	2006-03-08 17:45:56.000000000 +0100
@@ -392,7 +392,7 @@ class ListAdmin:
             text = Utils.maketext(
                 'subauth.txt',
                 {'username'   : addr,
-                 'listname'   : self.internal_name(),
+                 'listname'   : self.local_part,
                  'hostname'   : self.host_name,
                  'admindb_url': self.GetScriptURL('admindb', absolute=1),
                  }, mlist=self)
@@ -444,7 +444,7 @@ class ListAdmin:
             text = Utils.maketext(
                 'unsubauth.txt',
                 {'username'   : addr,
-                 'listname'   : self.internal_name(),
+                 'listname'   : self.local_part,
                  'hostname'   : self.host_name,
                  'admindb_url': self.GetScriptURL('admindb', absolute=1),
                  }, mlist=self)
diff -pru mailman-cvs-2.1-maint/Mailman/MailList.py mailman-2.1.7.cg/Mailman/MailList.py
--- mailman-cvs-2.1-maint/Mailman/MailList.py	2006-03-12 17:05:43.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/MailList.py	2006-03-09 10:24:43.000000000 +0100
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2005 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
@@ -186,8 +186,8 @@ class MailList(HTMLFormatter, Deliverer,
 
     def getListAddress(self, extra=None):
         if extra is None:
-            return '%s@%s' % (self.internal_name(), self.host_name)
-        return '%s-%s@%s' % (self.internal_name(), extra, self.host_name)
+            return '%s@%s' % (self.local_part, self.host_name)
+        return '%s-%s@%s' % (self.local_part, extra, self.host_name)
 
     # For backwards compatibility
     def GetBouncesEmail(self):
@@ -204,14 +204,13 @@ class MailList(HTMLFormatter, Deliverer,
 
     def GetConfirmEmail(self, cookie):
         return mm_cfg.VERP_CONFIRM_FORMAT % {
-            'addr'  : '%s-confirm' % self.internal_name(),
+            'addr'  : '%s-confirm' % self.local_part,
             'cookie': cookie,
             } + '@' + self.host_name
 
     def GetConfirmJoinSubject(self, listname, cookie):
         if mm_cfg.VERP_CONFIRMATIONS and cookie:
-            cset = i18n.get_translation().charset() or \
-                       Utils.GetCharSet(self.preferred_language)
+            cset = Utils.GetCharSet(self.preferred_language)
             subj = Header(
      _('Your confirmation is required to join the %(listname)s mailing list'),
                           cset, header_name='subject')
@@ -221,8 +220,7 @@ class MailList(HTMLFormatter, Deliverer,
 
     def GetConfirmLeaveSubject(self, listname, cookie):
         if mm_cfg.VERP_CONFIRMATIONS and cookie:
-            cset = i18n.get_translation().charset() or \
-                       Utils.GetCharSet(self.preferred_language)
+            cset = Utils.GetCharSet(self.preferred_language)
             subj = Header(
      _('Your confirmation is required to leave the %(listname)s mailing list'),
                           cset, header_name='subject')
@@ -250,8 +248,10 @@ class MailList(HTMLFormatter, Deliverer,
             return "%s%s@%s" % (acct, self.umbrella_member_suffix, host)
 
     def GetScriptURL(self, scriptname, absolute=0):
+        # Using "local_part" here works for both site wide lists on
+        # the default url host and for vhost lists on the vhost url host.
         return Utils.ScriptURL(scriptname, self.web_page_url, absolute) + \
-               '/' + self.internal_name()
+               '/' + self.local_part
 
     def GetOptionsURL(self, user, obscure=0, absolute=0):
         url = self.GetScriptURL('options', absolute)
@@ -295,6 +295,10 @@ class MailList(HTMLFormatter, Deliverer,
         # Non-configurable list info
         if name:
           self._internal_name = name
+        if '@' in self._internal_name:
+            self.local_part = self._internal_name.split('@')[0]
+        else:
+            self.local_part = self._internal_name
 
         # When was the list created?
         self.created_at = time.time()
@@ -344,8 +348,7 @@ class MailList(HTMLFormatter, Deliverer,
                 mm_cfg.DEFAULT_BOUNCE_MATCHING_HEADERS
         self.header_filter_rules = []
         self.anonymous_list = mm_cfg.DEFAULT_ANONYMOUS_LIST
-        internalname = self.internal_name()
-        self.real_name = internalname[0].upper() + internalname[1:]
+        self.real_name = self.local_part[0].upper() + self.local_part[1:]
         self.description = ''
         self.info = ''
         self.welcome_msg = ''
@@ -479,7 +482,13 @@ class MailList(HTMLFormatter, Deliverer,
         # the admin's email address, so transform the exception.
         if emailhost is None:
             emailhost = mm_cfg.DEFAULT_EMAIL_HOST
-        postingaddr = '%s@%s' % (name, emailhost)
+        if '@' in name:
+            firstname, emailhost = name.split('@', 1)
+        else:
+            firstname = name
+
+        # but we keep a sensible posting address
+        postingaddr = '%s@%s' % (firstname, emailhost)
         try:
             Utils.ValidateEmail(postingaddr)
         except Errors.MMBadEmailError:
@@ -487,11 +496,16 @@ class MailList(HTMLFormatter, Deliverer,
         # Validate the admin's email address
         Utils.ValidateEmail(admin)
         self._internal_name = name
+
         self._full_path = Site.get_listpath(name, create=1)
         # Don't use Lock() since that tries to load the non-existant config.pck
         self.__lock.lock()
         self.InitVars(name, admin, crypted_password)
         self.CheckValues()
+        # this is for getListAddress
+        self.list_address = postingaddr
+        self.real_name = firstname
+        self.subject_prefix = mm_cfg.DEFAULT_SUBJECT_PREFIX % self.__dict__
         if langs is None:
             self.available_languages = [self.preferred_language]
         else:
@@ -754,6 +768,15 @@ class MailList(HTMLFormatter, Deliverer,
             else:
                 goodtopics.append((name, pattern, desc, emptyflag))
         self.topics = goodtopics
+        # Define local_part if not defined yet (loaded ML in old data format).
+        if self.__dict__.has_key('local_part') and self.local_part:
+            pass
+        else:
+            _tmp = self.internal_name()
+            if '@' in _tmp:
+                self.local_part = _tmp.split('@')[0]
+            else:
+                self.local_part = _tmp
 
 
     #
@@ -969,7 +992,7 @@ class MailList(HTMLFormatter, Deliverer,
         else:
             kind = ''
         syslog('subscribe', '%s: new%s %s, %s', self.internal_name(),
-               kind, formataddr((name, email)), whence)
+               kind, formataddr((email, name)), whence)
         if ack:
             self.SendSubscribeAck(email, self.getMemberPassword(email),
                                   digest, text)
@@ -1322,7 +1345,7 @@ class MailList(HTMLFormatter, Deliverer,
         addresses in the recipient headers.
         """
         # This is the list's full address.
-        listfullname = '%s@%s' % (self.internal_name(), self.host_name)
+        listfullname = self.getListAddress()
         recips = []
         # Check all recipient addresses against the list's explicit addresses,
         # specifically To: Cc: and Resent-to:
@@ -1337,7 +1360,7 @@ class MailList(HTMLFormatter, Deliverer,
             addr = addr.lower()
             localpart = addr.split('@')[0]
             if (# TBD: backwards compatibility: deprecated
-                    localpart == self.internal_name() or
+                    localpart == self.local_part or
                     # exact match against the complete list address
                     addr == listfullname):
                 return True
diff -pru mailman-cvs-2.1-maint/Mailman/Message.py mailman-2.1.7.cg/Mailman/Message.py
--- mailman-cvs-2.1-maint/Mailman/Message.py	2006-03-10 11:36:50.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Message.py	2005-12-30 19:50:07.000000000 +0100
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2005 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
diff -pru mailman-cvs-2.1-maint/Mailman/MTA/Manual.py mailman-2.1.7.cg/Mailman/MTA/Manual.py
--- mailman-cvs-2.1-maint/Mailman/MTA/Manual.py	2005-12-30 19:50:08.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/MTA/Manual.py	2006-03-09 10:29:20.000000000 +0100
@@ -56,6 +56,7 @@ def create(mlist, cgi=False, nolock=Fals
     if mlist is None:
         return
     listname = mlist.internal_name()
+    assert('@' not in listname) # No Manual MTA support for vhost lists
     fieldsz = len(listname) + len('-unsubscribe')
     if cgi:
         # If a list is being created via the CGI, the best we can do is send
@@ -104,6 +105,7 @@ equivalent) file by adding the following
 
 def remove(mlist, cgi=False):
     listname = mlist.internal_name()
+    assert('@' not in listname) # No Manual MTA support for vhost lists
     fieldsz = len(listname) + len('-unsubscribe')
     if cgi:
         # If a list is being removed via the CGI, the best we can do is send
diff -pru mailman-cvs-2.1-maint/Mailman/MTA/Postfix.py mailman-2.1.7.cg/Mailman/MTA/Postfix.py
--- mailman-cvs-2.1-maint/Mailman/MTA/Postfix.py	2005-12-30 19:50:08.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/MTA/Postfix.py	2006-03-04 18:13:08.000000000 +0100
@@ -28,12 +28,13 @@ from Mailman import mm_cfg
 from Mailman import Utils
 from Mailman import LockFile
 from Mailman.i18n import _
-from Mailman.MTA.Utils import makealiases
+from Mailman.MTA.Utils import makealiases, _extensions
 from Mailman.Logging.Syslog import syslog
 
 LOCKFILE = os.path.join(mm_cfg.LOCK_DIR, 'creator')
 ALIASFILE = os.path.join(mm_cfg.DATA_DIR, 'aliases')
 VIRTFILE = os.path.join(mm_cfg.DATA_DIR, 'virtual-mailman')
+TRANSPORTFILE = os.path.join(mm_cfg.DATA_DIR, 'transport-mailman')
 
 try:
     True, False
@@ -43,21 +44,23 @@ except NameError:
 
 
 
-def _update_maps():
-    msg = 'command failed: %s (status: %s, %s)'
-    acmd = mm_cfg.POSTFIX_ALIAS_CMD + ' ' + ALIASFILE
-    status = (os.system(acmd) >> 8) & 0xff
+def _update_map_file(mapfile, command=mm_cfg.POSTFIX_MAP_CMD):
+    msg = 'command failed: %s (status: %s, %s)'        
+    cmdline = command + ' ' + mapfile
+    status = os.spawnlp(os.P_WAIT, command, command, mapfile)
     if status:
         errstr = os.strerror(status)
-        syslog('error', msg, acmd, status, errstr)
-        raise RuntimeError, msg % (acmd, status, errstr)
+        syslog('error', msg, cmdline, status, errstr)
+        raise RuntimeError, msg % (cmdline, status, errstr)
+
+
+
+def _update_maps():
+    _update_map_file(ALIASFILE, command = mm_cfg.POSTFIX_ALIAS_CMD)
     if os.path.exists(VIRTFILE):
-        vcmd = mm_cfg.POSTFIX_MAP_CMD + ' ' + VIRTFILE
-        status = (os.system(vcmd) >> 8) & 0xff
-        if status:
-            errstr = os.strerror(status)
-            syslog('error', msg, vcmd, status, errstr)
-            raise RuntimeError, msg % (vcmd, status, errstr)
+        _update_map_file(VIRTFILE)
+    if os.path.exists(TRANSPORTFILE):
+        _update_map_file(TRANSPORTFILE)
 
 
 
@@ -79,7 +82,31 @@ def clear():
 
 
 
-def _addlist(mlist, fp):
+def _generate_alias(mlist, ext=None):
+    listname = mlist.internal_name()
+    if '@' in listname:
+        foo = listname.split('@')
+        foo.reverse()
+        aliaslistname = '='.join(foo)
+    else:
+        aliaslistname = listname
+    if ext:
+        return aliaslistname + '-' + ext
+    else:
+        return aliaslistname
+
+
+def _uses_transport(mlist):
+    listname = mlist.internal_name()
+    if False: # XXX mm_cfg.VHOST_USES_TRANSPORTS ... TBD NDIM
+        return ('@' in listname) and (mlist.host_name not in
+                                      mm_cfg.POSTFIX_STYLE_VIRTUAL_DOMAINS)
+    else:
+        return False
+
+
+
+def _addalias(mlist, fp):
     # Set up the mailman-loop address
     loopaddr = Utils.ParseEmail(Utils.get_site_email(extra='loop'))[0]
     loopmbox = os.path.join(mm_cfg.DATA_DIR, 'owner-bounces.mbox')
@@ -101,13 +128,18 @@ def _addlist(mlist, fp):
     # exercise is to get the minimal aliases.db file into existance.
     if mlist is None:
         return
+    # Only generate aliases for site wide lists and for lists in
+    # mm_cfg.POSTFIX_STYLE_VIRTUAL_DOMAINS
+    if _uses_transport(mlist):
+        return
     listname = mlist.internal_name()
-    fieldsz = len(listname) + len('-unsubscribe')
     # The text file entries get a little extra info
     print >> fp, '# STANZA START:', listname
     print >> fp, '# CREATED:', time.ctime(time.time())
     # Now add all the standard alias entries
-    for k, v in makealiases(listname):
+    fieldsz = len('-unsubscribe')
+    for k, v in makealiases(_generate_alias(mlist),
+                            internal_listname = listname):
         # Format the text file nicely
         print >> fp, k + ':', ((fieldsz - len(k)) * ' ') + v
     # Finish the text file stanza
@@ -118,8 +150,6 @@ def _addlist(mlist, fp):
 
 def _addvirtual(mlist, fp):
     listname = mlist.internal_name()
-    fieldsz = len(listname) + len('-unsubscribe')
-    hostname = mlist.host_name
     # Set up the mailman-loop address
     loopaddr = Utils.get_site_email(mlist.host_name, extra='loop')
     loopdest = Utils.ParseEmail(loopaddr)[0]
@@ -140,14 +170,26 @@ def _addvirtual(mlist, fp):
 %s\t%s
 # LOOP ADDRESSES END
 """ % (loopaddr, loopdest)
+    # NDIM XXX What are those loop addrs for, and do we need them for each
+    # virtual domain?
+    
+    # Only generate virtual alias for lists in
+    # NDIM DBG print "_addvirtual", listname
+    if '@' not in listname or _uses_transport(mlist):
+        return
     # The text file entries get a little extra info
     print >> fp, '# STANZA START:', listname
     print >> fp, '# CREATED:', time.ctime(time.time())
     # Now add all the standard alias entries
-    for k, v in makealiases(listname):
-        fqdnaddr = '%s@%s' % (k, hostname)
+    addrs = [ (mlist.getListAddress(),
+               _generate_alias(mlist)) ] # XXX name space clash
+    for ext in _extensions:
+        addrs.append((mlist.getListAddress(ext),
+                      _generate_alias(mlist, ext=ext))) # XXX name space clash
+    fieldsz = len(mlist.getListAddress('-unsubscribe'))
+    for addr, target in addrs:
         # Format the text file nicely
-        print >> fp, fqdnaddr, ((fieldsz - len(k)) * ' '), k
+        print >> fp, addr, ((fieldsz - len(addr)) * ' '), target
     # Finish the text file stanza
     print >> fp, '# STANZA END:', listname
     print >> fp
@@ -198,6 +240,42 @@ def _check_for_virtual_loopaddr(mlist, f
 
 
 
+def _addtransport(mlist, fp):
+    # XXX Set up the mailman-loop address
+    #loopaddr = Utils.get_site_email(mlist.host_name, extra='loop')
+    #loopdest = Utils.ParseEmail(loopaddr)[0]
+    # Seek to the end of the text file, but if it's empty write the standard
+    # disclaimer, and the loop catch address.
+    fp.seek(0, 2)
+    if not fp.tell():
+        print >> fp, """\
+# This file is generated by Mailman, and is kept in sync with the binary hash
+# file transport-mailman.db.  YOU SHOULD NOT MANUALLY EDIT THIS FILE unless you
+# know what you're doing, and can keep the two files properly in sync.  If you
+# screw it up, you're on your own.
+
+"""
+    # Only generate transport for vhost list
+    if not _uses_transport(mlist):
+        return
+    # The text file entries get a little extra info
+    print >> fp, '# STANZA START:', listname
+    print >> fp, '# CREATED:', time.ctime(time.time())
+    # Now add all the standard alias entries
+    listname = mlist.internal_name()
+    fieldsz = len('-unsubscribe')
+    addrs = [ mlist.getListAddress() ]
+    for ext in _extensions:
+        addrs.append(mlist.getListAddress(ext))
+    for addr in addrs:
+        target = "mailman:"
+        print >> fp, addr, ((fieldsz - len(ext)) * ' '), target
+    # Finish the text file stanza
+    print >> fp, '# STANZA END:', listname
+    print >> fp
+
+
+
 def _do_create(mlist, textfile, func):
     # Crack open the plain text file
     try:
@@ -213,6 +291,8 @@ def _do_create(mlist, textfile, func):
         func(mlist, fp)
     finally:
         fp.close()
+
+    # XXX check transports?
     # Now double check the virtual plain text file
     if func is _addvirtual:
         _check_for_virtual_loopaddr(mlist, textfile)
@@ -226,9 +306,10 @@ def create(mlist, cgi=False, nolock=Fals
         lock.lock()
     # Do the aliases file, which need to be done in any case
     try:
-        _do_create(mlist, ALIASFILE, _addlist)
-        if mlist and mlist.host_name in mm_cfg.POSTFIX_STYLE_VIRTUAL_DOMAINS:
+        _do_create(mlist, ALIASFILE, _addalias)
+        if mlist and '@' in mlist.internal_name(): # NDIM XXX and mlist.host_name in mm_cfg.POSTFIX_STYLE_VIRTUAL_DOMAINS:
             _do_create(mlist, VIRTFILE, _addvirtual)
+        _do_create(mlist, TRANSPORTFILE, _addtransport)
         _update_maps()
     finally:
         if lock:
diff -pru mailman-cvs-2.1-maint/Mailman/MTA/Utils.py mailman-2.1.7.cg/Mailman/MTA/Utils.py
--- mailman-cvs-2.1-maint/Mailman/MTA/Utils.py	2005-08-27 03:40:16.000000000 +0200
+++ mailman-2.1.7.cg/Mailman/MTA/Utils.py	2006-02-22 12:26:47.000000000 +0100
@@ -22,6 +22,10 @@ import pwd
 from Mailman import mm_cfg
 
 
+_extensions = ('admin', 'bounces', 'confirm', 'join', 'leave', 'owner',
+               'request', 'subscribe', 'unsubscribe', )
+
+
 
 def getusername():
     username = os.environ.get('USER') or os.environ.get('LOGNAME')
@@ -34,7 +38,9 @@ def getusername():
 
 
 
-def _makealiases_mailprog(listname):
+def _makealiases_mailprog(listname, internal_listname=None):
+    if not internal_listname:
+        internal_listname = listname
     wrapper = os.path.join(mm_cfg.WRAPPER_DIR, 'mailman')
     # Most of the list alias extensions are quite regular.  I.e. if the
     # message is delivered to listname-foobar, it will be filtered to a
@@ -46,12 +52,11 @@ def _makealiases_mailprog(listname):
     #    need for the -admin address anymore).
     #
     # Seed this with the special cases.
-    aliases = [(listname,          '"|%s post %s"' % (wrapper, listname)),
+    aliases = [(listname,          '"|%s post %s"' % (wrapper, internal_listname)),
                ]
-    for ext in ('admin', 'bounces', 'confirm', 'join', 'leave', 'owner',
-                'request', 'subscribe', 'unsubscribe'):
+    for ext in _extensions:
         aliases.append(('%s-%s' % (listname, ext),
-                        '"|%s %s %s"' % (wrapper, ext, listname)))
+                        '"|%s %s %s"' % (wrapper, ext, internal_listname)))
     return aliases
 
 
@@ -66,8 +71,7 @@ def _makealiases_maildir(listname):
     # Note, don't use this unless your MTA leaves the envelope recipient in
     # Delivered-To:, Envelope-To:, or Apparently-To:
     aliases = [(listname, maildir)]
-    for ext in ('admin', 'bounces', 'confirm', 'join', 'leave', 'owner',
-                'request', 'subscribe', 'unsubscribe'):
+    for ext in _extensions:
         aliases.append(('%s-%s' % (listname, ext), maildir))
     return aliases
 
diff -pru mailman-cvs-2.1-maint/Mailman/Queue/ArchRunner.py mailman-2.1.7.cg/Mailman/Queue/ArchRunner.py
--- mailman-cvs-2.1-maint/Mailman/Queue/ArchRunner.py	2006-02-03 08:04:32.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Queue/ArchRunner.py	2005-08-27 03:40:16.000000000 +0200
@@ -49,7 +49,7 @@ class ArchRunner(Runner):
                 elif abs(now - mktime_tz(tup)) > \
                          mm_cfg.ARCHIVER_ALLOWABLE_SANE_DATE_SKEW:
                     clobber = 1
-            except (ValueError, OverflowError):
+            except ValueError:
                 # The likely cause of this is that the year in the Date: field
                 # is horribly incorrect, e.g. (from SF bug # 571634):
                 # Date: Tue, 18 Jun 0102 05:12:09 +0500
diff -pru mailman-cvs-2.1-maint/Mailman/Queue/BounceRunner.py mailman-2.1.7.cg/Mailman/Queue/BounceRunner.py
--- mailman-cvs-2.1-maint/Mailman/Queue/BounceRunner.py	2006-03-10 11:36:50.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Queue/BounceRunner.py	2006-03-09 19:49:12.000000000 +0100
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2004 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
@@ -12,8 +12,7 @@
 #
 # 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.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
 """Bounce queue runner."""
 
@@ -89,12 +88,9 @@ class BounceMixin:
     def _queue_bounces(self, listname, addrs, msg):
         today = time.localtime()[:3]
         if self._bounce_events_fp is None:
-            omask = os.umask(006)
-            try:
-                self._bounce_events_fp = open(self._bounce_events_file, 'a+b')
-            finally:
-                os.umask(omask)
+            self._bounce_events_fp = open(self._bounce_events_file, 'a+b')
         for addr in addrs:
+            # NDIM XXX: Where is this "listname" (aka internal_name()) read?
             cPickle.dump((listname, addr, today, msg),
                          self._bounce_events_fp, 1)
         self._bounce_events_fp.flush()
@@ -197,11 +193,7 @@ class BounceRunner(Runner, BounceMixin):
             return
         # Try VERP detection first, since it's quick and easy
         addrs = verp_bounce(mlist, msg)
-        if addrs:
-            # We have an address, but check if the message is non-fatal.
-            if BouncerAPI.ScanMessages(mlist, msg) is BouncerAPI.Stop:
-                return
-        else:
+        if not addrs:
             # See if this was a probe message.
             token = verp_probe(mlist, msg)
             if token:
@@ -210,9 +202,6 @@ class BounceRunner(Runner, BounceMixin):
             # That didn't give us anything useful, so try the old fashion
             # bounce matching modules.
             addrs = BouncerAPI.ScanMessages(mlist, msg)
-            if addrs is BouncerAPI.Stop:
-                # This is a recognized, non-fatal notice. Ignore it.
-                return
         # If that still didn't return us any useful addresses, then send it on
         # or discard it.
         if not addrs:
diff -pru mailman-cvs-2.1-maint/Mailman/Queue/NewsRunner.py mailman-2.1.7.cg/Mailman/Queue/NewsRunner.py
--- mailman-cvs-2.1-maint/Mailman/Queue/NewsRunner.py	2005-08-27 03:40:16.000000000 +0200
+++ mailman-2.1.7.cg/Mailman/Queue/NewsRunner.py	2006-03-09 19:44:04.000000000 +0100
@@ -38,7 +38,7 @@ mcre = re.compile(r"""
     \d+.                                          # serial number
     \d+.                                          # time in seconds since epoch
     \d+.                                          # pid
-    (?P<listname>[^@]+)                           # list's internal_name()
+    (?P<localpart>[^@]+)                          # list's local_part
     @                                             # localpart@dom.ain
     (?P<hostname>[^>]+)                           # list's host_name
     >                                             # trailer
@@ -139,8 +139,10 @@ def prepare_message(mlist, msg, msgdata)
     if msgid:
         mo = mcre.search(msgid)
         if mo:
-            lname, hname = mo.group('listname', 'hostname')
-            if lname == mlist.internal_name() and hname == mlist.host_name:
+            lname, hname = mo.group('localname', 'hostname')
+            if '@'.join(lname, hname) == mlist.internal_name():
+                hackmsgid = False
+            elif lname == mlist.internal_name() and hname == mlist.host_name:
                 hackmsgid = False
     if hackmsgid:
         del msg['message-id']
diff -pru mailman-cvs-2.1-maint/Mailman/SecurityManager.py mailman-2.1.7.cg/Mailman/SecurityManager.py
--- mailman-cvs-2.1-maint/Mailman/SecurityManager.py	2005-08-27 03:40:15.000000000 +0200
+++ mailman-2.1.7.cg/Mailman/SecurityManager.py	2006-03-04 12:27:50.000000000 +0100
@@ -86,6 +86,9 @@ class SecurityManager:
         # Non configurable
         self.passwords = {}
 
+    def _cookie_key(self):
+        return self.internal_name().replace('@','%40')
+
     def AuthContextInfo(self, authcontext, user=None):
         # authcontext may be one of AuthUser, AuthListModerator,
         # AuthListAdmin, AuthSiteAdmin.  Not supported is the AuthCreator
@@ -98,7 +101,8 @@ class SecurityManager:
         # AuthUser, but the user isn't a member of this mailing list, a
         # NotAMemberError will be raised.  If the user's secret is None, raise
         # a MMBadUserError.
-        key = self.internal_name() + '+'
+        # NDIM: XXX Does this have security implications?
+        key = self._cookie_key() + '+' 
         if authcontext == mm_cfg.AuthUser:
             if user is None:
                 # A bad system error
@@ -292,7 +296,7 @@ class SecurityManager:
                 usernames = [user]
             else:
                 usernames = []
-                prefix = self.internal_name() + '+user+'
+                prefix = self._cookie_key() + '+user+'
                 for k in c.keys():
                     if k.startswith(prefix):
                         usernames.append(k[len(prefix):])
diff -pru mailman-cvs-2.1-maint/Mailman/Site.py mailman-2.1.7.cg/Mailman/Site.py
--- mailman-cvs-2.1-maint/Mailman/Site.py	2005-08-27 03:40:15.000000000 +0200
+++ mailman-2.1.7.cg/Mailman/Site.py	2006-03-09 22:45:32.000000000 +0100
@@ -22,6 +22,7 @@ implementation should work for standard 
 
 import os
 import errno
+import sys
 
 from Mailman import mm_cfg
 
@@ -47,35 +48,57 @@ def _makedir(path):
 
 
 
-# BAW: We don't really support domain<>None yet.  This will be added in a
-# future version.  By default, Mailman will never pass in a domain argument.
-def get_listpath(listname, domain=None, create=0):
-    """Return the file system path to the list directory for the named list.
+def get_listsubdir(listname):
+    """Return subdirectory name for the named list.
 
-    If domain is given, it is the virtual domain for the named list.  The
-    default is to not distinguish list paths on the basis of virtual domains.
+    Append this subdirectory name to a subsystem specific directory
+    such as archives/private/ or similar.
+
+    The directory scheme implemented is like this:
+    
+        <subsystem directory>/
+           mylist0/            # one "site default list"
+           mylist1/            # another "site default list"
+           some.domain.com/
+              mylist1/         # a "vhost list"
+              mylist2/         # another "vhost list"
+           mydomain.com/
+              mylist1/         # yet another "vhost list"
+
+    Ideally, this function and get_listnames() below would be the only
+    code to modify if you want to introduce another disk storage
+    scheme, e.g. d/domain.com/mylist1 and m/mydomain.com/mylist1.
+    
+    See also get_listpath and get_archpath docs for example usage.
+    """
+    if '@' in listname:
+        tmp = listname.split('@')
+        return os.path.join(tmp[1],tmp[0])
+    else:
+        return listname
+    
+    
+
+def get_listpath(listname, create=0):
+    """Return the file system path to the list directory for the named list.
 
     If the create flag is true, then this method should create the path
     hierarchy if necessary.  If the create flag is false, then this function
     should not attempt to create the path heirarchy (and in fact the absence
     of the path might be significant).
     """
-    path = os.path.join(mm_cfg.LIST_DATA_DIR, listname)
+    path = os.path.join(mm_cfg.LIST_DATA_DIR,
+                        get_listsubdir(listname))
     if create:
         _makedir(path)
     return path
 
 
 
-# BAW: We don't really support domain<>None yet.  This will be added in a
-# future version.  By default, Mailman will never pass in a domain argument.
-def get_archpath(listname, domain=None, create=False, public=False):
+def get_archpath(listname, create=False, public=False):
     """Return the file system path to the list's archive directory for the
     named list in the named virtual domain.
 
-    If domain is given, it is the virtual domain for the named list.  The
-    default is to not distinguish list paths on the basis of virtual domains.
-
     If the create flag is true, then this method should create the path
     hierarchy if necessary.  If the create flag is false, then this function
     should not attempt to create the path heirarchy (and in fact the absence
@@ -88,15 +111,20 @@ def get_archpath(listname, domain=None, 
         subdir = mm_cfg.PUBLIC_ARCHIVE_FILE_DIR
     else:
         subdir = mm_cfg.PRIVATE_ARCHIVE_FILE_DIR
-    path = os.path.join(subdir, listname)
+    path = os.path.join(subdir, get_listsubdir(listname))
     if create:
         _makedir(path)
     return path
 
 
 
-# BAW: We don't really support domain<>None yet.  This will be added in a
-# future version.  By default, Mailman will never pass in a domain argument.
+def get_mboxpath(listname, create=False, public=False):
+    """Get path to archive mbox file"""
+    return os.path.join(get_archpath(listname) + '.mbox',
+                        listname.split('@')[0] + '.mbox')
+
+
+
 def get_listnames(domain=None):
     """Return the names of all the known lists for the given domain.
 
@@ -105,9 +133,19 @@ def get_listnames(domain=None):
     """
     # Import this here to avoid circular imports
     from Mailman.Utils import list_exists
-    # We don't currently support separate virtual domain directories
     got = []
-    for fn in os.listdir(mm_cfg.LIST_DATA_DIR):
-        if list_exists(fn):
-            got.append(fn)
+    datadir = mm_cfg.LIST_DATA_DIR
+    if domain:
+        domaindir = os.path.join(datadir, domain)
+        if os.path.isdir(domaindir):
+            for localpart in os.listdir(domaindir):
+                listname = '@'.join([localpart, domain])
+                if list_exists(listname):
+                    got.append(listname)
+    else:
+        for fn in os.listdir(datadir):
+            if list_exists(fn):
+                got.append(fn)
+            else:
+                got.extend(get_listnames(fn))
     return got
diff -pru mailman-cvs-2.1-maint/Mailman/Utils.py mailman-2.1.7.cg/Mailman/Utils.py
--- mailman-cvs-2.1-maint/Mailman/Utils.py	2005-08-27 03:40:15.000000000 +0200
+++ mailman-2.1.7.cg/Mailman/Utils.py	2006-03-10 16:05:32.000000000 +0100
@@ -218,6 +218,7 @@ def ValidateEmail(s):
 
 
 
+# Helper function for CGI scripts
 def GetPathPieces(envar='PATH_INFO'):
     path = os.environ.get(envar)
     if path:
@@ -226,6 +227,39 @@ def GetPathPieces(envar='PATH_INFO'):
 
 
 
+# Helper function for CGI scripts
+def _GetURLHost(envar='SERVER_NAME'):
+    # NDIM XXX: Not pretty logic.
+    web_server_name = os.environ.get(envar)
+    if web_server_name:
+        return web_server_name.lower()
+    return mm_cfg.DEFAULT_URL_HOST
+
+
+
+# Helper function for CGI scripts
+def _GetEmailHost(url_host=None):
+    return mm_cfg.VIRTUAL_HOSTS.get(url_host or _GetURLHost(),
+                                    mm_cfg.DEFAULT_EMAIL_HOST)
+
+
+
+# Helper function for CGI scripts
+def GetListName(parts=None):
+    # NDIM XXX Not pretty either
+    if not parts:
+        parts = GetPathPieces()
+    local_part = parts[0].lower()
+    url_host = _GetURLHost()
+
+    if url_host == mm_cfg.DEFAULT_URL_HOST:
+        return local_part
+    else:
+        email_host = _GetEmailHost(url_host=url_host)
+        return '@'.join([local_part,email_host])
+
+
+
 def ScriptURL(target, web_page_url=None, absolute=False):
     """target - scriptname only, nothing extra
     web_page_url - the list's configvar of the same name
@@ -530,6 +564,19 @@ def findtext(templatefile, dict=None, ra
     fp.close()
     text = template
     if dict is not None:
+        # Fix up incorrect template usage introduced in vhost branch:
+        #   - templates use %(listname)s@%(hostname)s patterns
+        #   - pre-vhost Mailman uses { 'listname': mlist.internal_name(),
+        #                              'hostname': mlist.host_name, }
+        #   - vhost Mailman uses     { 'listname': mlist.local_part,
+        #                              'hostname': mlist.host_name, }
+        # Just to make sure, we also log this, so we can fix the code.
+        if dict.has_key('listname'):
+            if '@' in dict['listname']:
+                from Mailman.Logging.Syslog import syslog
+                syslog('vhost', 'vhost warning: listname %s is using @ for template file %s',
+                       repr(dict['listname']), repr(templatefile))
+                dict['listname'] = dict['listname'].split('@')[0]
         try:
             sdict = SafeDict(dict)
             try:
@@ -706,7 +753,7 @@ def unique_message_id(mlist):
     global _serial
     msgid = '<mailman.%d.%d.%d.%s@%s>' % (
         _serial, time.time(), os.getpid(),
-        mlist.internal_name(), mlist.host_name)
+        mlist.local_part, mlist.host_name)
     _serial += 1
     return msgid
 
diff -pru mailman-cvs-2.1-maint/Mailman/Version.py mailman-2.1.7.cg/Mailman/Version.py
--- mailman-cvs-2.1-maint/Mailman/Version.py	2006-03-10 11:36:50.000000000 +0100
+++ mailman-2.1.7.cg/Mailman/Version.py	2005-12-31 12:43:56.000000000 +0100
@@ -15,7 +15,7 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
 # Mailman version
-VERSION = "2.1.8a1"
+VERSION = "2.1.7"
 
 # And as a hex number in the manner of PY_VERSION_HEX
 ALPHA = 0xa
@@ -27,8 +27,8 @@ FINAL = 0xf
 
 MAJOR_REV = 2
 MINOR_REV = 1
-MICRO_REV = 8
-REL_LEVEL = ALPHA
+MICRO_REV = 7
+REL_LEVEL = FINAL
 # at most 15 beta releases!
 REL_SERIAL = 1
 
diff -pru mailman-cvs-2.1-maint/messages/es/LC_MESSAGES/mailman.po mailman-2.1.7.cg/messages/es/LC_MESSAGES/mailman.po
--- mailman-cvs-2.1-maint/messages/es/LC_MESSAGES/mailman.po	2006-02-28 13:43:39.000000000 +0100
+++ mailman-2.1.7.cg/messages/es/LC_MESSAGES/mailman.po	2005-12-20 01:52:35.000000000 +0100
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Mailman\n"
 "POT-Creation-Date: Tue Dec 20 09:46:16 2005\n"
-"PO-Revision-Date: 2006-02-15 12:02+0100\n"
+"PO-Revision-Date: 2005-10-14 13:20+0200\n"
 "Last-Translator: David Martínez Moreno <ender@rediris.es>\n"
 "Language-Team: Español <es@li.org>\n"
 "MIME-Version: 1.0\n"
@@ -1016,7 +1016,7 @@ msgstr "Dirección hostil (caracteres no 
 
 #: Mailman/Cgi/admin.py:1350
 msgid "Banned address (matched %(pattern)s)"
-msgstr "Dirección vetada (coincidencia %(pattern)s)"
+msgstr ""
 
 #: Mailman/Cgi/admin.py:1356
 msgid "Successfully invited:"
@@ -1301,7 +1301,7 @@ msgstr " ya es un suscriptor"
 
 #: Mailman/Cgi/admindb.py:808
 msgid "%(addr)s is banned (matched: %(patt)s)"
-msgstr "%(addr)s está vetada (concordancia: %(patt)s)"
+msgstr ""
 
 #: Mailman/Cgi/confirm.py:75
 msgid "Confirmation string was empty."
@@ -1524,15 +1524,16 @@ msgid "You are already a member of this 
 msgstr "¡Ya está suscrito a esta lista de distribución!"
 
 #: Mailman/Cgi/confirm.py:375
+#, fuzzy
 msgid ""
 "You are currently banned from subscribing to\n"
 "            this list.  If you think this restriction is erroneous, please\n"
 "            contact the list owners at %(owneraddr)s."
 msgstr ""
-"La dirección de correo electrónico que ha suministrado se ha vetado\n"
+"La dirección de correo electrónico que está suministrando se ha vetado\n"
 "en esta lista de distribución. Si piensa que esta restricción está\n"
 "equivocada, por favor, póngase en contacto con el propietario de la\n"
-"lista en la dirección %(owneraddr)s."
+"lista en la dirección %(listowner)s."
 
 #: Mailman/Cgi/confirm.py:379
 msgid ""
@@ -1642,15 +1643,16 @@ msgid "You have canceled your change of 
 msgstr "Ha cancelado la solicitud de cambio de dirección"
 
 #: Mailman/Cgi/confirm.py:528
+#, fuzzy
 msgid ""
 "%(newaddr)s is banned from subscribing to the\n"
 "            %(realname)s list.  If you think this restriction is erroneous,\n"
 "            please contact the list owners at %(owneraddr)s."
 msgstr ""
-"A %(newaddr)s se le vetado la suscripción a la lista\n"
-"%(realname)s. Si piensa que esta restricción está\n"
+"La dirección de correo electrónico que está suministrando se ha vetado\n"
+"en esta lista de distribución. Si piensa que esta restricción está\n"
 "equivocada, por favor, póngase en contacto con el propietario de la\n"
-"lista en la dirección %(owneraddr)s."
+"lista en la dirección %(listowner)s."
 
 #: Mailman/Cgi/confirm.py:534
 msgid "Change of address request confirmed"
@@ -2335,13 +2337,11 @@ msgid ""
 "The list administrator may not view the other\n"
 "            subscriptions for this user."
 msgstr ""
-"El administrador de la lista puede que no vea las otras\n"
-"            suscripciones de este usuario."
 
 #: Mailman/Cgi/options.py:279 Mailman/Cgi/options.py:322
 #: Mailman/Cgi/options.py:442 Mailman/Cgi/options.py:658
 msgid "Note: "
-msgstr "Nota: "
+msgstr ""
 
 #: Mailman/Cgi/options.py:284
 msgid "List subscriptions for %(safeuser)s on %(hostname)s"
@@ -2361,10 +2361,6 @@ msgid ""
 "            or addresses for this user's other subscriptions.  However, the\n"
 "            subscription for this mailing list has been changed."
 msgstr ""
-"El administrador de la lista puede que no sea capaz\n"
-"            de cambiar los nombres o las direcciones\n"
-"            de este usuario en otras suscripciones.\n"
-"            No obstante, se han cambiado los datos para esta."
 
 #: Mailman/Cgi/options.py:342
 msgid "Addresses did not match!"
@@ -2413,15 +2409,16 @@ msgid "%(newaddr)s is already a member o
 msgstr "%(newaddr)s ya está suscrito a la lista."
 
 #: Mailman/Cgi/options.py:404
+#, fuzzy
 msgid ""
 "%(newaddr)s is banned from this list.  If you\n"
 "                      think this restriction is erroneous, please contact\n"
 "                      the list owners at %(owneraddr)s."
 msgstr ""
-"%(newaddr)s está vetada en esta lista de distribución.\n"
-"            Si piensa que esta restricción está equivocada,\n"
-"            por favor, póngase en contacto con el propietario\n"
-"            de la lista en %(owneraddr)s."
+"La dirección de correo electrónico que está suministrando se ha vetado\n"
+"en esta lista de distribución. Si piensa que esta restricción está\n"
+"equivocada, por favor, póngase en contacto con el propietario de la\n"
+"lista en %(listowner)s."
 
 #: Mailman/Cgi/options.py:415
 msgid "Member name successfully changed. "
@@ -2437,14 +2434,16 @@ msgid "Passwords did not match!"
 msgstr "Las claves no coinciden!"
 
 #: Mailman/Cgi/options.py:439
+#, fuzzy
 msgid ""
 "The list administrator may not change the\n"
 "            password for this user's other subscriptions.  However, the\n"
 "            password for this mailing list has been changed."
 msgstr ""
-"Puede que el administrador sea incapaz de cambiar la clave\n"
-"      de este usuario en otras suscripciones. No obstante,\n"
-"      se ha cambiado la clave en esta lista de distribución."
+"El administrador de la lista de distribución ha inhabilitado \n"
+"la entrega de los mensajes a medida que van llegando, por lo que \n"
+"la opción de entrega no se ha activado. Sin embargo, el resto \n"
+"de sus opciones se ha cambiado correctamente."
 
 #: Mailman/Cgi/options.py:456 Mailman/Commands/cmd_password.py:83
 #: Mailman/Commands/cmd_password.py:109
@@ -2500,10 +2499,6 @@ msgid ""
 "                options for this mailing list subscription has been\n"
 "                changed."
 msgstr ""
-"Puede que el administrador de la lista sea incapaz\n"
-"            de cambiar las opciones de este usuario en\n"
-"            otras suscripciones. No obstante, se han cambiado\n"
-"            los datos de la suscripción en esta lista."
 
 #: Mailman/Cgi/options.py:665
 msgid ""
@@ -2719,7 +2714,7 @@ msgstr "Debe especificar una lista."
 
 #: Mailman/Cgi/private.py:73
 msgid "Private archive - \"./\" and \"../\" not allowed in URL."
-msgstr "El archivo privado - \"./\" and \"../\" no se permiten en la URL."
+msgstr ""
 
 #: Mailman/Cgi/private.py:110
 msgid "Private Archive Error - %(msg)s"
@@ -3033,14 +3028,16 @@ msgstr ""
 "dirección de correo electrónico?"
 
 #: Mailman/Commands/cmd_confirm.py:68
+#, fuzzy
 msgid ""
 "You are currently banned from subscribing to this list.  If you think this\n"
 "restriction is erroneous, please contact the list owners at\n"
 "%(owneraddr)s."
 msgstr ""
-"Actualmente está vetado de suscribirse a esta lista. Si piensa que esta\n"
-"            restricción está equivocada, por favor, póngase en contacto\n"
-"            con el propietario de la lista en %(owneraddr)s."
+"La dirección de correo electrónico que está suministrando se ha vetado\n"
+"en esta lista de distribución. Si piensa que esta restricción está\n"
+"equivocada, por favor, póngase en contacto con el propietario de la\n"
+"lista en %(listowner)s."
 
 #: Mailman/Commands/cmd_confirm.py:73
 msgid ""
@@ -3801,7 +3798,7 @@ msgstr "Húngaro"
 
 #: Mailman/Defaults.py:1327
 msgid "Interlingua"
-msgstr "Interlingua"
+msgstr ""
 
 #: Mailman/Defaults.py:1328
 msgid "Italian"
@@ -3837,7 +3834,7 @@ msgstr "Portugués"
 
 #: Mailman/Defaults.py:1336
 msgid "Portuguese (Brazil)"
-msgstr "Portugués (Brasil)"
+msgstr "Portugués (Basil)"
 
 #: Mailman/Defaults.py:1337
 msgid "Romanian"
@@ -4238,8 +4235,8 @@ msgstr "Sensibilidad para la detección d
 #: Mailman/Gui/Bounce.py:78
 msgid "Should Mailman perform automatic bounce processing?"
 msgstr ""
-"¿Quiere que Mailman se encargue del procesamiento de los rebotes de "
-"forma automática?"
+"Debería realizar Mailman una <em>sincronización</em> con los grupos de "
+"noticias"
 
 #: Mailman/Gui/Bounce.py:79
 msgid ""
@@ -4481,6 +4478,7 @@ msgid "Preserve"
 msgstr "Preservar"
 
 #: Mailman/Gui/ContentFilter.py:42
+#, fuzzy
 msgid ""
 "Policies concerning the content of list traffic.\n"
 "\n"
@@ -4521,26 +4519,38 @@ msgid ""
 msgstr ""
 "Política concerniente al contenido del tráfico de la lista.\n"
 "\n"
-"        <p>El filtrado del contenido funciona de esta manera: cuando la lista\n"
-"        recibe un mensaje y ha habilitado el filtrado del contenido, en primer lugar\n"
-"        se comparan los adjuntos individuales con <a href=\"?VARHELP=contentfilter/filter_mime_types\">\n"
-"        tipos filtrados</a>. Si el tipo del adjunto coincide con alguno de los tipos filtrados,\n"
+"        <p>El filtrado del contenido funciona de esta manera: cuando la "
+"lista\n"
+"        recibe un mensaje y ha habilitado el filtrado del contenido, en "
+"primer lugar\n"
+"        se comparan los adjuntos individuales con <a href=\"?"
+"VARHELP=contentfilter/filter_mime_types\">\n"
+"        tipos filtrados</a>. Si el tipo del adjunto coincide con alguno de "
+"los tipos filtrados,\n"
 "        se descarta.\n"
 "\n"
-"        <p>A continuación, si hay definidos <a href=\\\"?VARHELP=contentfilter/pass_mime_types\\\">\n"
-"        tipos permitidos</a>, cualquier tipo del adjunto que <em>no</em> coincida con los tipos permitidos también\n"
-"        se descartan. Si no hay definidos tipos permitidos se omite esta comprobación.\n"
-"\n"
-"        <p>Después de este filtrado inicial, cualquier adjunto <tt>multipart</tt> que quede vacío\n"
-"        se borra. Si el mensaje saliente  queda vacio después de este filtrado, entonces se descarta\n"
-"        el mensaje entero. A continuación, si está habilitado\n"
-"        <a href=\"?VARHELP=contentfilter/collapse_alternatives\">collapse_alternatives</a>, cada sección\n"
-"        <tt>multipart/alternative</tt> se reemplaza con el primer alternativo que no esté vacio después\n"
-"        del filtrado.\n"
-"\n"
-"        <p>Finalmente, cualquier parte <tt>text/html</tt> que quede en el mensaje se convierte a\n"
-"        <tt>text/plain</tt> si <a href=\"?VARHELP=contentfilter/convert_html_to_plaintext\">\n"
-"        convert_html_to_plaintext</a> está habilitado y el servidor está configurado para permitir\n"
+"        <p>A continuación, si hay definidos <a href=\\\"?"
+"VARHELP=contentfilter/pass_mime_types\\\">\n"
+"        tipos permitidos</a>, cualquier tipo del adjunto que <em>no</em> "
+"coincida con los tipos permitidos también\n"
+"        se descartan. Si no hay definidos tipos permitidos se omite esta "
+"comprobación.\n"
+"\n"
+"        <p>Después de este filtrado inicial, cualquier adjunto "
+"<tt>multipart</tt> que quede vacío\n"
+"        se borra. Si el mensaje saliente  queda vacio después de este "
+"filtrado, entonces se\n"
+"        descarta el mensaje entero. A continuación, cada sección "
+"<tt>multipart/alternative</tt>\n"
+"        se reemplaza con el primer alternativo que no esté vacio después del "
+"filtrado.\n"
+"\n"
+"        <p>Finalmente, cualquier parte <tt>text/html</tt> que quede en el "
+"mensaje se convierte a\n"
+"        <tt>text/plain</tt> si <a href=\"?VARHELP=contentfilter/"
+"convert_html_to_plaintext\">\n"
+"        convert_html_to_plaintext</a> está habilitado y el servidor está "
+"configurado para permitir\n"
 "        estas conversiones."
 
 #: Mailman/Gui/ContentFilter.py:75
@@ -4645,12 +4655,13 @@ msgstr ""
 "filtro."
 
 #: Mailman/Gui/ContentFilter.py:117
+#, fuzzy
 msgid ""
 "Should Mailman collapse multipart/alternative to its\n"
 "             first part content?"
 msgstr ""
-"¿Debería Mailman reducir los adjuntos multipart/alternative al\n"
-"             contenido que aparezca en primer lugar?"
+"¿Debería mandar Mailman una respuesta automática a los suscriptores de la \n"
+"             lista de distribución?"
 
 #: Mailman/Gui/ContentFilter.py:121
 msgid ""
@@ -4747,7 +4758,7 @@ msgstr "Características de la entrega de
 #: Mailman/Gui/Digest.py:47
 msgid "Can list members choose to receive list traffic bunched in digests?"
 msgstr ""
-"¿Pueden elegir los suscriptores recibir el tráfico de la lista agrupado en un "
+"¿Pueden elegir los suscriptores recibir el tráfico de la lista agupado en un "
 "solo mensaje? (digest)"
 
 #: Mailman/Gui/Digest.py:51
@@ -5216,6 +5227,7 @@ msgstr ""
 "recomienda <em> enérgicamente</em>."
 
 #: Mailman/Gui/General.py:172
+#, fuzzy
 msgid ""
 "This option controls what Mailman does to the\n"
 "             <tt>Reply-To:</tt> header in messages flowing through this\n"
@@ -5260,32 +5272,42 @@ msgstr ""
 "             <tt>Reply-To:</tt> de los mensajes que pasan por esta lista de\n"
 "             distribución. Cuando esté puesto al valor <em>Remitente</em>\n"
 "             Mailman no añadirá ninguna cabecera <tt>Reply-To:</tt>,\n"
-"             aunque si el mensaje original ya contiene una no se\n"
-"             quitará. Poniendo este valor a <em>Esta lista</em> o a\n"
+"             aunque si el mensaje original ya contiene una, ésta no se\n"
+"             quitará Poniendo este valor a <em>Esta lista</em> o a\n"
 "             <em>Dirección explícita</em> hace que Mailman inserte una\n"
 "             cabecera específica en todos los mensajes, sobreescribiendo la\n"
-"             cabecera del mensaje original si fuese necesario (<em>Dirección\n"
+"             cabecera del mensaje original si fuese necesario "
+"(<em>Dirección\n"
 "             explícita</em> inserta el valor \n"
-"             <a href=\"?VARHELP=general/reply_to_address\">reply_to_address</a>).\n"
+"             <a href=\"?VARHELP=general/reply_to_address\">reply_to_address</"
+"a>)\n"
 " \n"
-"             <p>Hay muchas razones para no presentar o sobresescribir la cabecera\n"
+"             <p>Hay muchas razones para no presentar o sobresescribir la "
+"cabecera\n"
 "             <tt>Reply-To:</tt>. Una de ellas es porque algunos remitentes\n"
 "             dependen de su propia configuración <tt>Reply-To:</tt> para\n"
-"             conseguir transmitir su dirección de respuesta. Otra razón se debe a que\n"
-"             modificando la cabecera <tt>Reply-To:</tt> hace que sea más dificil\n"
+"             conseguir transmitir su dirección de respuesta. Otra razón se "
+"debe a que\n"
+"             modificando la cabecera <tt>Reply-To:</tt> hace que sea más "
+"dificil\n"
 "             mandar respuestas privadas. Véase \n"
-"             <a href=\"http://www.unicom.com/pw/reply-to-harmful.html\">`Reply-To' Munging\n"
-"             Considered Harmful </a> para una discusión general sobre este tema.\n"
-"             Véase <a href=\"http://www.metasystema.net/essays/reply-to.mhtml\">Reply-To\n"
-"             Munging Considered Useful</a> para ver una opinión contraria.\n"
+"             <a href=\"http://www.unicom.com/pw/reply-to-harmful.html"
+"\">`Reply-To' Munging\n"
+"             Considered Harmful </a> para una discusión general sobre este "
+"tema\n"
 "             \n"
-"             <p>Algunas listas de distribución tienen restringida la capacidad de\n"
+"             <p>Algunas listas de distribución tienen restringida la "
+"capacidad de\n"
 "             mandar mensajes, \n"
 "             con una lista paralela dedicada a discusiones. Como ejemplo\n"
-"             están las listas `patches' o `checkin', donde los cambios al software\n"
-"             se envían por un sistema de control de versiones, pero las discusiones\n"
-"             sobre los cambios ocurren en una lista de distribución de desarrolladores.\n"
-"             Para poder tratar estos tipos de listas de distribución, seleccione\n"
+"             están las listas `patches' o `checkin', donde los cambios al "
+"software\n"
+"             se envían por un sistema de control de versiones, pero las "
+"discusiones\n"
+"             sobre los cambios ocurren en una lista de distribución de "
+"desarrolladores.\n"
+"             Para poder tratar estos tipos de listas de distribución, "
+"seleccione\n"
 "             <tt>Dirección explícita</tt> e indique la dirección\n"
 "             <tt>Reply-To:</tt> debajo para apuntar a la lista paralela."
 
@@ -5294,6 +5316,7 @@ msgid "Explicit <tt>Reply-To:</tt> heade
 msgstr "Cabecera explícita <tt>Reply-To:</tt>"
 
 #: Mailman/Gui/General.py:206
+#, fuzzy
 msgid ""
 "This is the address set in the <tt>Reply-To:</tt> header\n"
 "             when the <a\n"
@@ -5335,20 +5358,24 @@ msgstr ""
 " Esta es la opción que se pone en la cabecera <tt>Reply-To:</tt>\n"
 "             cuando la opción\n"
 "             <a href=\"?VARHELP=general/reply_goes_to_list\">\n"
-"             reply_goes_to_list</a> está puesta a <em>Dirección explícita</em>. \n"
+"             reply_goes_to_list</a> está puesta a <em>Dirección              "
+"explícita</em>. \n"
 "\n"
 "             <p>Hay muchas razones para no presentar o sobresescribir la\n"
 "             cabecera <tt>Reply-To:</tt>. Una de ellas es porque algunos\n"
-"             envíos a la lista dependen de su propia configuración\n"
-"             <tt>Reply-To:</tt> para conseguir que las respuestas lleguen.\n"
-"             Otra razón se debe a que modificando la cabecera\n"
-"             <tt>Reply-To:</tt> hace que sea más dificil mandar respuestas\n"
-"             privadas. Véase \n"
+"             envíos a la lista\n"
+"             dependen de su propia configuración\n"
+"             <tt>Reply-To:</tt> para\n"
+"             conseguir que las respuestas lleguen. Otra razón se\n"
+"             debe a que modificando la cabecera <tt>Reply-To:</tt> hace\n"
+"             que sea más dificil mandar respuestas privadas.\n"
+"             Véase \n"
 "             <a href=\"http://www.unicom.com/pw/reply-to-harmful.html\">\n"
 "             `Reply-To' Munging Considered Harmful </a> para una\n"
 "             discusión general sobre este tema. Vea\n"
-"             href=\"http://www.metasystema.org/essays/reply-to-useful.mhtml\">Reply-To\n"
-"             Munging Considered Useful</a> para ver una opinión contraria.\n"
+"        href=\"http://www.metasystema.org/essays/reply-to-useful.mhtml"
+"\">Reply-To\n"
+"             Munging Considered Useful</a> para ver una opinión disidente.\n"
 "\n"
 "             <p>Algunas listas de distribución tienen restringida\n"
 "             la capacidad de mandar mensajes, con una lista paralela\n"
@@ -5358,12 +5385,14 @@ msgstr ""
 "             pero las discusiones sobre los cambios ocurren en una lista\n"
 "             de distribución de desarrolladores. Para poder tratar\n"
 "             estos tipos de listas de distribución, especificar\n"
-"             la direccioón explícita <tt>Reply-To:</tt> aquí. También\n"
-"             tiene que indicar <tt>Dirección explícita</tt> en la opción\n"
-"             <tt>reply_goes_to_list</tt>.\n"
+"             la direccioón explícita <tt>Reply-To:</tt>\n"
+"             aquí. También tiene que indicar\n"
+"             <tt>Dirección explícita</tt>\n"
+"             en la opción <tt>reply_goes_to_list</tt>\n"
 "\n"
 "             <p>Observe que si el mensaje original contiene ya una\n"
-"             cabecera <tt>Reply-To:</tt>, dicha cabecera no se reemplazará"
+"             cabecera <tt>Reply-To:</tt>,\n"
+"             dicha cabecera no se reemplazará"
 
 #: Mailman/Gui/General.py:235
 msgid "Umbrella list settings"
@@ -6974,7 +7003,7 @@ msgstr ""
 "correspondencia,\n"
 "              éste se rechazará, retendrá o descartará en función de la "
 "acción que\n"
-"              indique.   Use <em>Diferir</em> para inhabilitar temporalmente "
+"              indique.   Use <em>Defer</em> para inhabilitar temporalmente "
 "una regla.\n"
 "\n"
 "              Puede tener más de una regla de filtrado en su lista. En ese "
@@ -8207,8 +8236,9 @@ msgstr ""
 "el propietario de%(dbfile)s es %(owner)s (tiene que pertenecer a %(user)s"
 
 #: Mailman/MTA/Postfix.py:354
+#, fuzzy
 msgid "%(dbfile)s permissions must be 066x (got %(octmode)s)"
-msgstr "Los permisos de %(dbfile)s deberían ser 066x (y son %(octmode)s)"
+msgstr "Los permisos de %(file)s deberían ser 066x (y son %(octmode)s)"
 
 #: Mailman/MailList.py:214
 msgid "Your confirmation is required to join the %(listname)s mailing list"
@@ -9024,11 +9054,6 @@ msgid ""
 "         If you're on a shared multiuser system, you should consult the\n"
 "         installation manual on how to fix this."
 msgstr ""
-"Atención: El directorio donde reside el archivo privado es ejecutable\n"
-"          por el resto del mundo (o+x). Esto podría hacer que otros\n"
-"          usuarios de su sistema pudieran leer el archivo privado.\n"
-"          Si está en un entorno multiusuario debería consultar el manual\n"
-"          de instalación para ver como solventar este tema."
 
 #: bin/check_perms:232
 msgid "mbox file must be at least 0660:"
@@ -9449,14 +9474,13 @@ msgstr ""
 "\n"
 
 #: bin/config_list:116
+#, fuzzy
 msgid ""
 "# -*- python -*-\n"
 "# -*- coding: %(charset)s -*-\n"
 "## \"%(listname)s\" mailing list configuration settings\n"
 "## captured on %(when)s\n"
 msgstr ""
-"# -*- python -*-\n"
-"# -*- codificación: %(charset)s -*-\n"
 "## valores de configuración de la lista de distribución \"%(listname)s\"\n"
 "## -*- python -*-\n"
 "## capturados el %(when)s\n"
@@ -11796,8 +11820,9 @@ msgid "updating old qfiles"
 msgstr "actualizando los qfiles antiguos"
 
 #: bin/update:448
+#, fuzzy
 msgid "Warning!  Not a directory: %(dirpath)s"
-msgstr "¡Atención! No es un directorio: %(dirpath)s"
+msgstr "El directorio que representa la cola es incorrecto: %(qdir)s"
 
 #: bin/update:517
 msgid "message is unparsable: %(filebase)s"
@@ -11805,7 +11830,7 @@ msgstr "El mensaje es inanalizable: %(fi
 
 #: bin/update:531
 msgid "Warning!  Deleting empty .pck file: %(pckfile)s"
-msgstr "¡Atención! Borrando el fichero .pck vacio: %(pckfile)s"
+msgstr ""
 
 #: bin/update:550
 msgid "Updating Mailman 2.0 pending_subscriptions.db database"
diff -pru mailman-cvs-2.1-maint/misc/Makefile.in mailman-2.1.7.cg/misc/Makefile.in
--- mailman-cvs-2.1-maint/misc/Makefile.in	2006-03-10 11:36:51.000000000 +0100
+++ mailman-2.1.7.cg/misc/Makefile.in	2005-08-27 03:40:17.000000000 +0200
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2005 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
@@ -12,8 +12,7 @@
 # 
 # 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.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
 # NOTE: Makefile.in is converted into Makefile by the configure script
 # in the parent directory.  Once configure has run, you can recreate
@@ -53,7 +52,7 @@ SETUPINSTOPTS=	--install-lib $(DESTDIR)$
 SETUPCMD=	setup.py --quiet install $(SETUPINSTOPTS)
 PKGDIR= 	$(srcdir)
 
-EMAILPKG=	email-2.5.7
+EMAILPKG=	email-2.5.6
 JACODECSPKG=	JapaneseCodecs-1.4.11
 KOCODECSPKG=	KoreanCodecs-2.0.5
 
diff -pru mailman-cvs-2.1-maint/NEWS mailman-2.1.7.cg/NEWS
--- mailman-cvs-2.1-maint/NEWS	2006-03-10 11:36:50.000000000 +0100
+++ mailman-2.1.7.cg/NEWS	2005-12-31 12:43:55.000000000 +0100
@@ -4,40 +4,6 @@ Copyright (C) 1998-2005 by the Free Soft
 
 Here is a history of user visible changes to Mailman.
 
-2.1.8a1 (10-Mar-2006)
-
-  Bug fixes and other patches
-
-    - Bouncers support added: 'unknown user', Microsoft SMTPSVC, Prodigy.net
-      and several others.
-
-    - Updated email library to 2.5.7 which will encode payload into qp/base64
-      upon setting.  This enabled backing out the scrubber related patches
-      including 'X-Mailman-Scrubbed' header in 2.1.7.
-
-    - Fix SpamDetect.py potential hold/reject loop problem.
-
-    - A warning message from email package to the stderr can cause error
-      in Logging because stderr may be detached from the process during
-      the qrunner run.  We chose not to output errors to stderr but to
-      the logs/error if the process is running under mailmanctl subprocess.
-
-    - DKIM header cleansing was separated from Cleanse.py and added to
-      -owner messages too.
-
-    - Fixes: Lose Topics when go directly to topics URL (1194419).
-      UnicodeError running bin/arch (1395683).  edithtml.py missing import
-      (1400128).  Bad escape in cleanarch.  Wrong timezone in list archive
-      index pages (1433673).  bin/arch fails with TypeError (1430236).
-      Subscription fails with some Language combinations (1435722).
-      Postfix delayed notification not recognized (863989).  2.1.7 (VERP)
-      mistakes delay notice for bounce (1421285).  show_qfiles: 'str'
-      object has no attribute 'as_string' (1444447).
-
-  Miscellaneous
-
-    - Brad Knowles' mailman daily status report script updated to 0.0.16.
-
 2.1.7 (31-Dec-2005)
 
   Security
diff -pru mailman-cvs-2.1-maint/tests/test_bounces.py mailman-2.1.7.cg/tests/test_bounces.py
--- mailman-cvs-2.1-maint/tests/test_bounces.py	2006-03-10 11:37:01.000000000 +0100
+++ mailman-2.1.7.cg/tests/test_bounces.py	2005-08-27 03:40:17.000000000 +0200
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2001,2002 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
@@ -12,8 +12,7 @@
 #
 # 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.
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
 """Test the bounce detection modules."""
 
@@ -22,8 +21,6 @@ import os
 import unittest
 import email
 
-from Mailman.Bouncers.BouncerAPI import Stop
-
 
 
 class BounceTest(unittest.TestCase):
@@ -43,24 +40,14 @@ class BounceTest(unittest.TestCase):
         ('SimpleMatch', 'simple_04.txt', ['claird@starbase.neosoft.com']),
         ('SimpleMatch', 'newmailru_01.txt', ['zzzzz@newmail.ru']),
         ('SimpleMatch', 'hotpop_01.txt', ['allensmithee@hotpop.com']),
-        ('SimpleMatch', 'microsoft_03.txt', ['midica@banknbr.com']),
-        ('SimpleMatch', 'simple_05.txt', ['rlosardo@sbcglobal.net']),
-        ('SimpleMatch', 'simple_06.txt', ['dlyle@hamiltonpacific.com']),
-        ('SimpleMatch', 'simple_07.txt', ['william.xxxx@sbcglobal.net']),
-        ('SimpleMatch', 'simple_08.txt', ['severin.XXX@t-online.de']),
-        ('SimpleMatch', 'simple_09.txt', ['RobotMail@auto-walther.de']),
-        ('SimpleMatch', 'simple_10.txt', ['sais@thehartford.com']),
-        ('SimpleMatch', 'simple_11.txt', ['carlosr73@hartfordlife.com']),
         # SimpleWarning
-        ('SimpleWarning', 'simple_03.txt', Stop),
+        ('SimpleWarning', 'simple_03.txt', ['jacobus@geo.co.za']),
         # GroupWise
         ('GroupWise', 'groupwise_01.txt', ['thoff@MAINEX1.ASU.EDU']),
         # This one really sucks 'cause it's text/html.  Just make sure it
         # doesn't throw an exception, but we won't get any meaningful
         # addresses back from it.
         ('GroupWise', 'groupwise_02.txt', []),
-        # Actually, it's from Exchange, and Exchange does recognize it
-        ('Exchange', 'groupwise_02.txt', ['omarmo@thebas.com']),
         # Yale's own
         ('Yale', 'yale_01.txt', ['thomas.dtankengine@cs.yale.edu',
                                  'thomas.dtankengine@yale.edu']),
@@ -69,14 +56,13 @@ class BounceTest(unittest.TestCase):
         ('DSN', 'dsn_02.txt', ['zzzzz@zeus.hud.ac.uk']),
         ('DSN', 'dsn_03.txt', ['ddd.kkk@advalvas.be']),
         ('DSN', 'dsn_04.txt', ['max.haas@unibas.ch']),
-        ('DSN', 'dsn_05.txt', Stop),
-        ('DSN', 'dsn_06.txt', Stop),
-        ('DSN', 'dsn_07.txt', Stop),
-        ('DSN', 'dsn_08.txt', Stop),
+        ('DSN', 'dsn_05.txt', ['pkocmid@atlas.cz']),
+        ('DSN', 'dsn_06.txt', ['hao-nghi.au@fr.thalesgroup.com']),
+        ('DSN', 'dsn_07.txt', ['david.farrar@parliament.govt.nz']),
+        ('DSN', 'dsn_08.txt', ['news-list.zope@localhost.bln.innominate.de']),
         ('DSN', 'dsn_09.txt', ['pr@allen-heath.com']),
         ('DSN', 'dsn_10.txt', ['anne.person@dom.ain']),
         ('DSN', 'dsn_11.txt', ['joem@example.com']),
-        ('DSN', 'dsn_12.txt', ['auaauqdgrdz@jtc-con.co.jp']),
         # Microsoft Exchange
         ('Exchange', 'microsoft_01.txt', ['DJBENNETT@IKON.COM']),
         ('Exchange', 'microsoft_02.txt', ['MDMOORE@BALL.COM']),
@@ -84,13 +70,8 @@ class BounceTest(unittest.TestCase):
         ('SMTP32', 'smtp32_01.txt', ['oliver@pcworld.com.ph']),
         ('SMTP32', 'smtp32_02.txt', ['lists@mail.spicynoodles.com']),
         ('SMTP32', 'smtp32_03.txt', ['borisk@gw.xraymedia.com']),
-        ('SMTP32', 'smtp32_04.txt', ['after_another@pacbell.net',
-                                     'one_bad_address@pacbell.net']),
-        ('SMTP32', 'smtp32_05.txt', ['jmrpowersports@jmrpowersports.com']),
-        ('SMTP32', 'smtp32_06.txt', ['Absolute_garbage_addr@pacbell.net']),
         # Qmail
         ('Qmail', 'qmail_01.txt', ['psadisc@wwwmail.n-h.de']),
-        ('Qmail', 'qmail_02.txt', ['rauschlo@frontfin.com']),
         # LLNL's custom Sendmail
         ('LLNL', 'llnl_01.txt', ['trotts1@llnl.gov']),
         # Netscape's server...
@@ -120,9 +101,6 @@ class BounceTest(unittest.TestCase):
                                    'yuli_kolesnikov@yahoo.com']),
         ('Yahoo', 'yahoo_09.txt', ['hankel_o_fung@yahoo.com',
                                    'ultravirus2001@yahoo.com']),
-        ('Yahoo', 'yahoo_10.txt', ['jajcchoo@yahoo.com',
-                                   'lyons94706@yahoo.com',
-                                   'turtle4jne@yahoo.com']),
         # sina.com appears to use their own weird SINAEMAIL MTA
         ('Sina', 'sina_01.txt', ['boboman76@sina.com', 'alan_t18@sina.com']),
         # No address can be detected in these...
@@ -143,12 +121,8 @@ class BounceTest(unittest.TestCase):
             # Some modules return None instead of [] for failure
             if foundaddrs is None:
                 foundaddrs = []
-            if foundaddrs is not Stop:
-                # MAS: The following strip() is only because of my
-                # hybrid test environment.  It is not otherwise needed.
-                foundaddrs = [found.strip() for found in foundaddrs]
-                addrs.sort()
-                foundaddrs.sort()
+            addrs.sort()
+            foundaddrs.sort()
             self.assertEqual(addrs, foundaddrs)
 
     def test_SMTP32_failure(self):
