Index: Mailman/Archiver/pipermail.py
--- Mailman/Archiver/pipermail.py.orig	2009-02-23 22:23:35 +0100
+++ Mailman/Archiver/pipermail.py	2009-03-27 11:55:18 +0100
@@ -122,9 +122,9 @@
         parentID = article.parentID
         if parentID is not None and self.articleIndex.has_key(parentID):
             parent = self.getArticle(archive, parentID)
-            myThreadKey = parent.threadKey + article.date + '-'
+            myThreadKey = parent.threadKey + article.date + '/' + article.msgid + '-'
         else:
-            myThreadKey = article.date + '-'
+            myThreadKey = article.date + '/' + article.msgid + '-'
         article.threadKey = myThreadKey
         key = myThreadKey, article.msgid
         self.setThreadKey(archive, key, article.msgid)
@@ -418,7 +418,7 @@
                 else:
                     parent = self.database.getArticle(self.archive,
                                                     article.parentID)
-                    article.threadKey = parent.threadKey+article.date+'-'
+                    article.threadKey = parent.threadKey + article.date + '/' + article.msgid + '-'
                 self.database.setThreadKey(self.archive,
                     (article.threadKey, article.msgid),
                     msgid)
@@ -632,9 +632,9 @@
             article.parentID = parentID = self.get_parent_info(arch, article)
             if parentID:
                 parent = self.database.getArticle(arch, parentID)
-                article.threadKey = parent.threadKey + article.date + '-'
+                article.threadKey = parent.threadKey + article.date + '/' + article.msgid + '-'
             else:
-                article.threadKey = article.date + '-'
+                article.threadKey = article.date + '/' + article.msgid + '-'
             key = article.threadKey, article.msgid
 
             self.database.setThreadKey(arch, key, article.msgid)
Index: Mailman/Commands/cmd_subscribe.py
--- Mailman/Commands/cmd_subscribe.py.orig	2009-02-23 22:23:35 +0100
+++ Mailman/Commands/cmd_subscribe.py	2009-03-27 11:55:18 +0100
@@ -84,6 +84,7 @@
     if password is None:
         password = Utils.MakeRandomPassword()
     if address is None:
+        h = None
         realname, address = parseaddr(res.msg['from'])
         if not address:
             # Fall back to the sender address
Index: Mailman/HTMLFormatter.py
--- Mailman/HTMLFormatter.py.orig	2009-02-23 22:23:35 +0100
+++ Mailman/HTMLFormatter.py	2009-03-27 11:55:18 +0100
@@ -44,7 +44,7 @@
         realname = self.real_name
         hostname = self.host_name
         listinfo_link  = Link(self.GetScriptURL('listinfo'), realname).Format()
-        owner_link = Link('mailto:' + self.GetOwnerEmail(), ownertext).Format()
+        owner_link = Link('mailto:' + self.GetOwnerEmail(), realname + '-owner').Format()
         innertext = _('%(listinfo_link)s list run by %(owner_link)s')
         return Container(
             '<hr>',
Index: Mailman/Handlers/Decorate.py
--- Mailman/Handlers/Decorate.py.orig	2009-02-23 22:23:35 +0100
+++ Mailman/Handlers/Decorate.py	2009-03-27 11:55:18 +0100
@@ -205,6 +205,7 @@
     del msg['content-transfer-encoding']
     del msg['content-disposition']
     msg['Content-Type'] = 'multipart/mixed'
+    msg['Mime-version'] = '1.0'
 
 
 
Index: Mailman/Handlers/Scrubber.py
--- Mailman/Handlers/Scrubber.py.orig	2009-02-23 22:23:35 +0100
+++ Mailman/Handlers/Scrubber.py	2009-03-27 11:55:18 +0100
@@ -388,6 +388,8 @@
                     t = unicode(t, 'ascii', 'replace')
                 try:
                     # Should use HTML-Escape, or try generalizing to UTF-8
+                    if len(charset) == 0:
+                        charset = 'us-ascii'
                     t = t.encode(charset, 'replace')
                 except (UnicodeError, LookupError, ValueError,
                         AssertionError):
Index: Mailman/Queue/OutgoingRunner.py
--- Mailman/Queue/OutgoingRunner.py.orig	2009-02-23 22:23:35 +0100
+++ Mailman/Queue/OutgoingRunner.py	2009-03-27 11:55:18 +0100
@@ -89,6 +89,7 @@
                 syslog('error', 'Cannot connect to SMTP server %s on port %s',
                        mm_cfg.SMTPHOST, port)
                 self.__logged = True
+            self._snooze(0)
             return True
         except Errors.SomeRecipientsFailed, e:
             # Handle local rejects of probe messages differently.
Index: Mailman/htmlformat.py
--- Mailman/htmlformat.py.orig	2009-02-23 22:23:35 +0100
+++ Mailman/htmlformat.py	2009-03-27 11:55:18 +0100
@@ -300,7 +300,8 @@
         charset = 'us-ascii'
         if self.language:
             charset = Utils.GetCharSet(self.language)
-        output = ['Content-Type: text/html; charset=%s\n' % charset]
+        output = ['Content-Type: text/html; charset=%s' % charset]
+        output.append('Cache-control: no-cache\n')
         if not self.suppress_head:
             kws.setdefault('bgcolor', self.bgcolor)
             tab = ' ' * indent
Index: bin/check_perms
--- bin/check_perms.orig	2009-02-23 22:23:35 +0100
+++ bin/check_perms	2009-03-27 11:55:18 +0100
@@ -82,7 +82,7 @@
     return os.stat(path)[ST_MODE]
 
 def statgidmode(path):
-    stat = os.stat(path)
+    stat = os.lstat(path)
     return stat[ST_MODE], stat[ST_GID]
 
 seen = {}
Index: bin/config_list
--- bin/config_list.orig	2009-02-23 22:23:35 +0100
+++ bin/config_list	2009-03-27 11:55:18 +0100
@@ -307,6 +307,11 @@
                                     in mm_cfg.OPTINFO.items()
                                     if validval & bitval]
                     gui._setValue(mlist, k, validval, fakedoc)
+                    # Ugly hack, but seems to be needed since
+                    # new_member_options isn't really a number in gui.
+                    # -- tfheen, 2003-12-06
+                    if k == "new_member_options":
+                        mlist.new_member_options = validval
             # BAW: when to do gui._postValidate()???
     finally:
         if savelist and not checkonly:
Index: bin/mailmanctl
--- bin/mailmanctl.orig	2009-02-23 22:23:35 +0100
+++ bin/mailmanctl	2009-03-27 11:55:18 +0100
@@ -417,6 +417,13 @@
         # won't be opening any terminal devices, don't do the ultra-paranoid
         # suggestion of doing a second fork after the setsid() call.
         os.setsid()
+
+        # Be sure to close any open std{in,out,err}
+        devnull = os.open('/dev/null', 0)
+        os.dup2(devnull, 0)
+        os.dup2(devnull, 1)
+        os.dup2(devnull, 2)
+
         # Instead of cd'ing to root, cd to the Mailman installation home
         os.chdir(mm_cfg.PREFIX)
         # Set our file mode creation umask
Index: bin/newlist
--- bin/newlist.orig	2009-02-23 22:23:35 +0100
+++ bin/newlist	2009-03-27 11:58:07 +0100
@@ -88,12 +88,16 @@
 defined in your Defaults.py file or overridden by settings in mm_cfg.py).
 
 Note that listnames are forced to lowercase.
+
+The list admin address need to be a fully-qualified address, like
+owner@example.com, not just owner.
 """
 
 import sys
 import os
 import getpass
 import getopt
+import grp
 
 import paths
 from Mailman import mm_cfg
@@ -122,6 +126,9 @@
 
 
 def main():
+    gid = grp.getgrnam(mm_cfg.MAILMAN_GROUP)[2]
+    if os.getgid() != mm_cfg.MAILMAN_GROUP:
+        os.setgid(gid)
     try:
         opts, args = getopt.getopt(sys.argv[1:], 'hql:u:e:',
                                    ['help', 'quiet', 'language=',
@@ -203,7 +210,7 @@
         except Errors.BadListNameError, s:
             usage(1, _('Illegal list name: %(s)s'))
         except Errors.EmailAddressError, s:
-            usage(1, _('Bad owner email address: %(s)s'))
+            usage(1, _('Bad owner email address: %(s)s.  Owner addresses need to be fully-qualified names, like "owner@example.com", not just "owner".'))
         except Errors.MMListAlreadyExistsError:
             usage(1, _('List already exists: %(listname)s'))
 
Index: bin/update
--- bin/update.orig	2009-02-23 22:23:35 +0100
+++ bin/update	2009-03-27 11:55:18 +0100
@@ -551,9 +551,11 @@
     file20 = os.path.join(mm_cfg.DATA_DIR, 'pending_subscriptions.db')
     file214 = os.path.join(mm_cfg.DATA_DIR, 'pending.pck')
     db = None
+    ver = None
     # Try to load the Mailman 2.0 file
     try:
         fp = open(file20)
+        ver = "20"
     except IOError, e:
         if e.errno <> errno.ENOENT: raise
     else:
@@ -565,6 +567,7 @@
         # Try to load the Mailman 2.1.x where x < 5, file
         try:
             fp = open(file214)
+            ver = "214"
         except IOError, e:
             if e.errno <> errno.ENOENT: raise
         else:
@@ -598,8 +601,12 @@
             # data[0] is the address being unsubscribed
             addrops_by_address.setdefault(data[0], []).append((key, val))
         elif op == Pending.SUBSCRIPTION:
-            # data[0] is a UserDesc object
-            addr = data[0].address
+            if ver == "20":
+                # data is tuple (emailaddr, password, digest)
+                addr = data[0]
+            else:
+                # data[0] is a UserDesc object
+                addr = data[0].address
             subs_by_address.setdefault(addr, []).append((key, val))
         elif op == Pending.RE_ENABLE:
             # data[0] is the mailing list's internal name
Index: scripts/driver
--- scripts/driver.orig	2009-02-23 22:23:35 +0100
+++ scripts/driver	2009-03-27 11:55:18 +0100
@@ -98,6 +98,15 @@
         module = getattr(pkg, scriptname)
         main = getattr(module, 'main')
         try:
+            import os
+            request_method = os.environ.get('REQUEST_METHOD')
+            if not request_method in ['GET', 'POST', 'HEAD']:
+                print "Status: 405 Method not allowed"
+                print "Content-type: text/plain"
+                print
+                print "The method is not allowed"
+                sys.exit()
+                
             try:
                 sys.stderr = logger
                 sys.stdout = tempstdout
