Revision: 8131
          http://svn.sourceforge.net/mailman/?rev=8131&view=rev
Author:   bwarsaw
Date:     2007-01-04 22:47:39 -0800 (Thu, 04 Jan 2007)

Log Message:
-----------
Clean up file permissions and umask settings.  Now we set the umask to 007
during early initialization so that we're guaranteed to get the right value
regardless of the shell umask used to invoke the command line script.  While
we're at it, we can remove almost all individual umask settings previously in
the code, and make file permissions consistently -rw-rw---- (IOW, files are no
longer other readable).

The only subsystem that wasn't changed was the archiver, because it uses its
own umask settings to ensure that private archives have the proper
permissions.  Eventually we'll mess with this, but if it ain't broken...

Note that check_perms complains about directory permissions, but I think
check_perms can be fixed (or perhaps, even removed?!).  If we decide to use
LMTPRunner and HTTPRunner exclusively then no outside process will be touching
our files potentially with the incorrect permissions, umask, owner, or group.
If we control all of our own touch points then I think we can lock out
'other'.

Another open question is whether Utils.set_global_password() can have its
umask setting removed.  It locks permissions down so even the group can't
write to the site password file, but the default umask of 007 might be good
enough even for this file.

Utils.makedirs() now takes an optional mode argument, which defaults to 02775
for backward compatibility.  First, the default mode can probably be changed
to 02770 (see above).  Second, all code that was tweaking the umask in order
to do a platform compatible os.mkdir() has now been refactored to use
Utils.makedirs().

Another tricky thing was getting SQLite via SQLAlchemy to create its
data/mailman.db file with the proper permissions.  From the comment in
dbcontext.py:

        # XXX By design of SQLite, database file creation does not honor
        # umask.  See their ticket #1193:
        # http://www.sqlite.org/cvstrac/tktview?tn=1193,31

More details in that file, but the work around is to essentially 'touch' the
database file if 'sqlite' is the scheme of the SQLAlchemy URL.  This little
pre-touch sets the right umask honoring permission and won't hurt if the file
already exists.  SQLite will happily keep the existing permissions, and in
fact that ticket referenced above recommends doing things this way.

In the Mailman.database.initialize(), create a global lock that prevents more
than one process from entering this init function at the same time.  It's
probably not strictly necessary given that I believe all the operations in
dbcontext.connect() are multi-processing safe, but it also doesn't seem to
hurt and prevents race conditions regardless of the database's own
safeguards (or lack thereof).

Make sure nightly_gzip.py calls initialize().

Modified Paths:
--------------
    trunk/mailman/Mailman/Cgi/create.py
    trunk/mailman/Mailman/Cgi/edithtml.py
    trunk/mailman/Mailman/Handlers/Scrubber.py
    trunk/mailman/Mailman/Handlers/ToDigest.py
    trunk/mailman/Mailman/ListAdmin.py
    trunk/mailman/Mailman/LockFile.py
    trunk/mailman/Mailman/MTA/Postfix.py
    trunk/mailman/Mailman/Pending.py
    trunk/mailman/Mailman/Queue/BounceRunner.py
    trunk/mailman/Mailman/Queue/HTTPRunner.py
    trunk/mailman/Mailman/Queue/Switchboard.py
    trunk/mailman/Mailman/Utils.py
    trunk/mailman/Mailman/bin/genaliases.py
    trunk/mailman/Mailman/bin/import.py
    trunk/mailman/Mailman/bin/mailmanctl.py
    trunk/mailman/Mailman/bin/newlist.py
    trunk/mailman/Mailman/bin/nightly_gzip.py
    trunk/mailman/Mailman/bin/update.py
    trunk/mailman/Mailman/database/__init__.py
    trunk/mailman/Mailman/database/dbcontext.py
    trunk/mailman/Mailman/initialize.py

Modified: trunk/mailman/Mailman/Cgi/create.py
===================================================================
--- trunk/mailman/Mailman/Cgi/create.py 2007-01-02 02:42:34 UTC (rev 8130)
+++ trunk/mailman/Mailman/Cgi/create.py 2007-01-05 06:47:39 UTC (rev 8131)
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2007 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
@@ -17,7 +17,6 @@
 
 """Create mailing lists through the web."""
 
-import os
 import cgi
 import sha
 import sys
@@ -162,15 +161,8 @@
     mlist = MailList.MailList()
     try:
         pw = sha.new(password).hexdigest()
-        # Guarantee that all newly created files have the proper permission.
-        # proper group ownership should be assured by the autoconf script
-        # enforcing that all directories have the group sticky bit set
-        oldmask = os.umask(002)
         try:
-            try:
-                mlist.Create(fqdn_listname, owner, pw, langs)
-            finally:
-                os.umask(oldmask)
+            mlist.Create(fqdn_listname, owner, pw, langs)
         except Errors.EmailAddressError, s:
             request_creation(doc, cgidata, _('Bad owner email address: $s'))
             return

Modified: trunk/mailman/Mailman/Cgi/edithtml.py
===================================================================
--- trunk/mailman/Mailman/Cgi/edithtml.py       2007-01-02 02:42:34 UTC (rev 
8130)
+++ trunk/mailman/Mailman/Cgi/edithtml.py       2007-01-05 06:47:39 UTC (rev 
8131)
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2007 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
@@ -165,14 +165,7 @@
     code = re.sub(r'<([/]?script.*?)>', r'&lt;\1&gt;', code)
     langdir = os.path.join(mlist.fullpath(), mlist.preferred_language)
     # Make sure the directory exists
-    omask = os.umask(0)
-    try:
-        try:
-            os.mkdir(langdir, 02775)
-        except OSError, e:
-            if e.errno <> errno.EEXIST: raise
-    finally:
-        os.umask(omask)
+    Utils.makedirs(langdir)
     fp = open(os.path.join(langdir, template_name), 'w')
     try:
         fp.write(code)

Modified: trunk/mailman/Mailman/Handlers/Scrubber.py
===================================================================
--- trunk/mailman/Mailman/Handlers/Scrubber.py  2007-01-02 02:42:34 UTC (rev 
8130)
+++ trunk/mailman/Mailman/Handlers/Scrubber.py  2007-01-05 06:47:39 UTC (rev 
8131)
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2007 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
@@ -176,11 +176,7 @@
             # TK: if part is attached then check charset and scrub if none
             if part.get('content-disposition') and \
                not part.get_content_charset():
-                omask = os.umask(002)
-                try:
-                    url = save_attachment(mlist, part, dir)
-                finally:
-                    os.umask(omask)
+                url = save_attachment(mlist, part, dir)
                 filename = part.get_filename(_('not available'))
                 filename = Utils.oneline(filename, lcset)
                 replace_payload_by_text(part, _("""\
@@ -204,11 +200,7 @@
                 # Pull it out as an attachment but leave it unescaped.  This
                 # is dangerous, but perhaps useful for heavily moderated
                 # lists.
-                omask = os.umask(002)
-                try:
-                    url = save_attachment(mlist, part, dir, filter_html=False)
-                finally:
-                    os.umask(omask)
+                url = save_attachment(mlist, part, dir, filter_html=False)
                 replace_payload_by_text(part, _("""\
 An HTML attachment was scrubbed...
 URL: %(url)s
@@ -229,11 +221,7 @@
                 # We're replacing the payload with the decoded payload so this
                 # will just get in the way.
                 del part['content-transfer-encoding']
-                omask = os.umask(002)
-                try:
-                    url = save_attachment(mlist, part, dir, filter_html=False)
-                finally:
-                    os.umask(omask)
+                url = save_attachment(mlist, part, dir, filter_html=False)
                 replace_payload_by_text(part, _("""\
 An HTML attachment was scrubbed...
 URL: %(url)s
@@ -241,11 +229,7 @@
         elif ctype == 'message/rfc822':
             # This part contains a submessage, so it too needs scrubbing
             submsg = part.get_payload(0)
-            omask = os.umask(002)
-            try:
-                url = save_attachment(mlist, part, dir)
-            finally:
-                os.umask(omask)
+            url = save_attachment(mlist, part, dir)
             subject = submsg.get('subject', _('no subject'))
             date = submsg.get('date', _('no date'))
             who = submsg.get('from', _('unknown sender'))
@@ -273,11 +257,7 @@
             if payload is None:
                 continue
             size = len(payload)
-            omask = os.umask(002)
-            try:
-                url = save_attachment(mlist, part, dir)
-            finally:
-                os.umask(omask)
+            url = save_attachment(mlist, part, dir)
             desc = part.get('content-description', _('not available'))
             filename = part.get_filename(_('not available'))
             filename = Utils.oneline(filename, lcset)

Modified: trunk/mailman/Mailman/Handlers/ToDigest.py
===================================================================
--- trunk/mailman/Mailman/Handlers/ToDigest.py  2007-01-02 02:42:34 UTC (rev 
8130)
+++ trunk/mailman/Mailman/Handlers/ToDigest.py  2007-01-05 06:47:39 UTC (rev 
8131)
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2007 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
@@ -67,11 +67,7 @@
     if not mlist.digestable or msgdata.get('isdigest'):
         return
     mboxfile = os.path.join(mlist.fullpath(), 'digest.mbox')
-    omask = os.umask(007)
-    try:
-        mboxfp = open(mboxfile, 'a+')
-    finally:
-        os.umask(omask)
+    mboxfp = open(mboxfile, 'a+')
     mbox = Mailbox(mboxfp)
     mbox.AppendMessage(msg)
     # Calculate the current size of the accumulation file.  This will not tell

Modified: trunk/mailman/Mailman/ListAdmin.py
===================================================================
--- trunk/mailman/Mailman/ListAdmin.py  2007-01-02 02:42:34 UTC (rev 8130)
+++ trunk/mailman/Mailman/ListAdmin.py  2007-01-05 06:47:39 UTC (rev 8131)
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2007 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,17 +98,13 @@
             # should we be as paranoid as for the config.pck file?  Should we
             # use pickle?
             tmpfile = self.__filename + '.tmp'
-            omask = os.umask(002)
+            fp = open(tmpfile, 'w')
             try:
-                fp = open(tmpfile, 'w')
-                try:
-                    cPickle.dump(self.__db, fp, 1)
-                    fp.flush()
-                    os.fsync(fp.fileno())
-                finally:
-                    fp.close()
+                cPickle.dump(self.__db, fp, 1)
+                fp.flush()
+                os.fsync(fp.fileno())
             finally:
-                os.umask(omask)
+                fp.close()
             self.__db = None
             # Do the dance
             os.rename(tmpfile, self.__filename)
@@ -191,21 +187,17 @@
         else:
             ext = 'txt'
         filename = 'heldmsg-%s-%d.%s' % (self.internal_name(), id, ext)
-        omask = os.umask(002)
+        fp = open(os.path.join(config.DATA_DIR, filename), 'w')
         try:
-            fp = open(os.path.join(config.DATA_DIR, filename), 'w')
-            try:
-                if config.HOLD_MESSAGES_AS_PICKLES:
-                    cPickle.dump(msg, fp, 1)
-                else:
-                    g = Generator(fp)
-                    g(msg, 1)
-                fp.flush()
-                os.fsync(fp.fileno())
-            finally:
-                fp.close()
+            if config.HOLD_MESSAGES_AS_PICKLES:
+                cPickle.dump(msg, fp, 1)
+            else:
+                g = Generator(fp)
+                g(msg, 1)
+            fp.flush()
+            os.fsync(fp.fileno())
         finally:
-            os.umask(omask)
+            fp.close()
         # save the information to the request database.  for held message
         # entries, each record in the database will be of the following
         # format:

Modified: trunk/mailman/Mailman/LockFile.py
===================================================================
--- trunk/mailman/Mailman/LockFile.py   2007-01-02 02:42:34 UTC (rev 8130)
+++ trunk/mailman/Mailman/LockFile.py   2007-01-05 06:47:39 UTC (rev 8131)
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2007 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
@@ -369,15 +369,11 @@
 
     def _write(self):
         # Make sure it's group writable
-        oldmask = os.umask(002)
-        fp = None
+        fp = open(self._tmpfname, 'w')
         try:
-            fp = open(self._tmpfname, 'w')
             fp.write(self._tmpfname)
         finally:
-            if fp:
-                fp.close()
-            os.umask(oldmask)
+            fp.close()
 
     def _read(self):
         try:

Modified: trunk/mailman/Mailman/MTA/Postfix.py
===================================================================
--- trunk/mailman/Mailman/MTA/Postfix.py        2007-01-02 02:42:34 UTC (rev 
8130)
+++ trunk/mailman/Mailman/MTA/Postfix.py        2007-01-05 06:47:39 UTC (rev 
8131)
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2007 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
@@ -174,12 +174,8 @@
     if func is _addtransport:
         loopdest = 'local:' + loopdest
     infp = open(filename)
-    omask = os.umask(007)
+    outfp = open(filename + '.tmp', 'w')
     try:
-        outfp = open(filename + '.tmp', 'w')
-    finally:
-        os.umask(omask)
-    try:
         # Find the start of the loop address block
         while True:
             line = infp.readline()
@@ -264,11 +260,7 @@
         fp = open(textfile, 'r+')
     except IOError, e:
         if e.errno <> errno.ENOENT: raise
-        omask = os.umask(007)
-        try:
-            fp = open(textfile, 'w+')
-        finally:
-            os.umask(omask)
+        fp = open(textfile, 'w+')
     try:
         func(mlist, fp)
     finally:
@@ -313,11 +305,7 @@
         # Otherwise, there's no text file to filter so we're done.
         return
     try:
-        omask = os.umask(007)
-        try:
-            outfp = open(textfile + '.tmp', 'w')
-        finally:
-            os.umask(omask)
+        outfp = open(textfile + '.tmp', 'w')
         filteroutp = False
         start = '# STANZA START: [EMAIL PROTECTED]' % (listname, hostname)
         end = '# STANZA END: [EMAIL PROTECTED]' % (listname, hostname)

Modified: trunk/mailman/Mailman/Pending.py
===================================================================
--- trunk/mailman/Mailman/Pending.py    2007-01-02 02:42:34 UTC (rev 8130)
+++ trunk/mailman/Mailman/Pending.py    2007-01-05 06:47:39 UTC (rev 8131)
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2007 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
@@ -113,18 +113,14 @@
                 del evictions[cookie]
         db['version'] = config.PENDING_FILE_SCHEMA_VERSION
         tmpfile = '%s.tmp.%d.%d' % (self.__pendfile, os.getpid(), now)
-        omask = os.umask(007)
+        fp = open(tmpfile, 'w')
         try:
-            fp = open(tmpfile, 'w')
-            try:
-                cPickle.dump(db, fp)
-                fp.flush()
-                os.fsync(fp.fileno())
-            finally:
-                fp.close()
-            os.rename(tmpfile, self.__pendfile)
+            cPickle.dump(db, fp)
+            fp.flush()
+            os.fsync(fp.fileno())
         finally:
-            os.umask(omask)
+            fp.close()
+        os.rename(tmpfile, self.__pendfile)
 
     def pend_confirm(self, cookie, expunge=True):
         """Return data for cookie, or None if not found.

Modified: trunk/mailman/Mailman/Queue/BounceRunner.py
===================================================================
--- trunk/mailman/Mailman/Queue/BounceRunner.py 2007-01-02 02:42:34 UTC (rev 
8130)
+++ trunk/mailman/Mailman/Queue/BounceRunner.py 2007-01-05 06:47:39 UTC (rev 
8131)
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2007 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
@@ -86,11 +86,7 @@
     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:
             cPickle.dump((listname, addr, today, msg),
                          self._bounce_events_fp, 1)

Modified: trunk/mailman/Mailman/Queue/HTTPRunner.py
===================================================================
--- trunk/mailman/Mailman/Queue/HTTPRunner.py   2007-01-02 02:42:34 UTC (rev 
8130)
+++ trunk/mailman/Mailman/Queue/HTTPRunner.py   2007-01-05 06:47:39 UTC (rev 
8131)
@@ -61,6 +61,7 @@
 
 
 qlog.info('HTTPRunner qrunner started.')
+hlog.info('HTTPRunner listening on %s:%s', config.HTTP_HOST, config.HTTP_PORT)
 try:
     server.serve_forever()
 # Do it this way because of exception hierarchy changes in Python 2.5.  XXX

Modified: trunk/mailman/Mailman/Queue/Switchboard.py
===================================================================
--- trunk/mailman/Mailman/Queue/Switchboard.py  2007-01-02 02:42:34 UTC (rev 
8130)
+++ trunk/mailman/Mailman/Queue/Switchboard.py  2007-01-05 06:47:39 UTC (rev 
8131)
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2007 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
@@ -65,15 +65,7 @@
     def __init__(self, whichq, slice=None, numslices=1, recover=False):
         self.__whichq = whichq
         # Create the directory if it doesn't yet exist.
-        # FIXME
-        omask = os.umask(0)                       # rwxrws---
-        try:
-            try:
-                os.mkdir(self.__whichq, 0770)
-            except OSError, e:
-                if e.errno <> errno.EEXIST: raise
-        finally:
-            os.umask(omask)
+        Utils.makedirs(self.__whichq, 0770)
         # Fast track for no slices
         self.__lower = None
         self.__upper = None
@@ -122,18 +114,14 @@
         # object or not.
         data['_parsemsg'] = (protocol == 0)
         # Write to the pickle file the message object and metadata.
-        omask = os.umask(007)                     # -rw-rw----
+        fp = open(tmpfile, 'w')
         try:
-            fp = open(tmpfile, 'w')
-            try:
-                fp.write(msgsave)
-                cPickle.dump(data, fp, protocol)
-                fp.flush()
-                os.fsync(fp.fileno())
-            finally:
-                fp.close()
+            fp.write(msgsave)
+            cPickle.dump(data, fp, protocol)
+            fp.flush()
+            os.fsync(fp.fileno())
         finally:
-            os.umask(omask)
+            fp.close()
         os.rename(tmpfile, filename)
         return filebase
 

Modified: trunk/mailman/Mailman/Utils.py
===================================================================
--- trunk/mailman/Mailman/Utils.py      2007-01-02 02:42:34 UTC (rev 8130)
+++ trunk/mailman/Mailman/Utils.py      2007-01-05 06:47:39 UTC (rev 8131)
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2007 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
@@ -347,6 +347,7 @@
     else:
         filename = config.LISTCREATOR_PW_FILE
     # rw-r-----
+    # XXX Is the default umask of 007 good enough?
     omask = os.umask(026)
     try:
         fp = open(filename, 'w')
@@ -641,11 +642,11 @@
 
 
 
-def makedirs(path):
+def makedirs(path, mode=02775):
     try:
         omask = os.umask(0)
         try:
-            os.makedirs(path, 02775)
+            os.makedirs(path, mode)
         finally:
             os.umask(omask)
     except OSError, e:

Modified: trunk/mailman/Mailman/bin/genaliases.py
===================================================================
--- trunk/mailman/Mailman/bin/genaliases.py     2007-01-02 02:42:34 UTC (rev 
8130)
+++ trunk/mailman/Mailman/bin/genaliases.py     2007-01-05 06:47:39 UTC (rev 
8131)
@@ -1,6 +1,6 @@
 #! @PYTHON@
 #
-# Copyright (C) 2001-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2007 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
@@ -17,7 +17,6 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
 # USA.
 
-import os
 import sys
 import optparse
 
@@ -72,9 +71,6 @@
     for listname in Utils.list_names():
         mlist = MailList.MailList(listname, lock=False)
         mlists.setdefault(mlist.host_name, []).append(mlist)
-    # Make sure the files are created rw-rw-xxx; it should be okay to be world
-    # readable.
-    omask = os.umask(002)
     try:
         MTA.clear()
         if not mlists:
@@ -86,7 +82,6 @@
                     # Be verbose for only the first printed list
                     quiet = True
     finally:
-        os.umask(omask)
         lock.unlock(unconditionally=True)
 
 

Modified: trunk/mailman/Mailman/bin/import.py
===================================================================
--- trunk/mailman/Mailman/bin/import.py 2007-01-02 02:42:34 UTC (rev 8130)
+++ trunk/mailman/Mailman/bin/import.py 2007-01-05 06:47:39 UTC (rev 8131)
@@ -48,12 +48,12 @@
     return u''
 
 
-def nodegen(node, *entities):
+def nodegen(node, *elements):
     for child in node.childNodes:
         if child.nodeType <> minidom.Node.ELEMENT_NODE:
             continue
-        if entities and child.tagName not in entities:
-            print _('Ignoring unexpected entity: $node.tagName')
+        if elements and child.tagName not in elements:
+            print _('Ignoring unexpected element: $node.tagName')
         else:
             yield child
 

Modified: trunk/mailman/Mailman/bin/mailmanctl.py
===================================================================
--- trunk/mailman/Mailman/bin/mailmanctl.py     2007-01-02 02:42:34 UTC (rev 
8130)
+++ trunk/mailman/Mailman/bin/mailmanctl.py     2007-01-05 06:47:39 UTC (rev 
8131)
@@ -1,4 +1,4 @@
-# Copyright (C) 2001-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2007 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
@@ -261,7 +261,7 @@
         args.extend(['-C', opts.config])
     os.execl(*args)
     # Should never get here
-    raise RuntimeError, 'os.execl() failed'
+    raise RuntimeError('os.execl() failed')
 
 
 def start_all_runners():
@@ -371,23 +371,18 @@
             return
         # child
         lock._take_possession()
-        # First, save our pid in a file for "mailmanctl stop" rendezvous.  We
-        # want the perms on the .pid file to be rw-rw----
-        omask = os.umask(6)
+        # Save our pid in a file for "mailmanctl stop" rendezvous.
+        fp = open(config.PIDFILE, 'w')
         try:
-            fp = open(config.PIDFILE, 'w')
             print >> fp, os.getpid()
-            fp.close()
         finally:
-            os.umask(omask)
+            fp.close()
         # Create a new session and become the session leader, but since we
         # 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()
         # Instead of cd'ing to root, cd to the Mailman installation home
         os.chdir(config.PREFIX)
-        # Set our file mode creation umask
-        os.umask(007)
         # I don't think we have any unneeded file descriptors.
         #
         # Now start all the qrunners.  This returns a dictionary where the

Modified: trunk/mailman/Mailman/bin/newlist.py
===================================================================
--- trunk/mailman/Mailman/bin/newlist.py        2007-01-02 02:42:34 UTC (rev 
8130)
+++ trunk/mailman/Mailman/bin/newlist.py        2007-01-05 06:47:39 UTC (rev 
8131)
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2007 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
@@ -15,7 +15,6 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
 # USA.
 
-import os
 import sha
 import sys
 import getpass
@@ -148,27 +147,20 @@
     mlist.preferred_language = opts.language
     try:
         pw = sha.new(listpasswd).hexdigest()
-        # Guarantee that all newly created files have the proper permission.
-        # proper group ownership should be assured by the autoconf script
-        # enforcing that all directories have the group sticky bit set.
-        oldmask = os.umask(002)
         try:
-            try:
-                mlist.Create(fqdn_listname, owner_mail, pw)
-            except Errors.BadListNameError, s:
-                parser.print_help()
-                print >> sys.stderr, _('Illegal list name: $s')
-                sys.exit(1)
-            except Errors.EmailAddressError, s:
-                parser.print_help()
-                print >> sys.stderr, _('Bad owner email address: $s')
-                sys.exit(1)
-            except Errors.MMListAlreadyExistsError:
-                parser.print_help()
-                print >> sys.stderr, _('List already exists: $listname')
-                sys.exit(1)
-        finally:
-            os.umask(oldmask)
+            mlist.Create(fqdn_listname, owner_mail, pw)
+        except Errors.BadListNameError, s:
+            parser.print_help()
+            print >> sys.stderr, _('Illegal list name: $s')
+            sys.exit(1)
+        except Errors.EmailAddressError, s:
+            parser.print_help()
+            print >> sys.stderr, _('Bad owner email address: $s')
+            sys.exit(1)
+        except Errors.MMListAlreadyExistsError:
+            parser.print_help()
+            print >> sys.stderr, _('List already exists: $listname')
+            sys.exit(1)
         mlist.Save()
     finally:
         mlist.Unlock()

Modified: trunk/mailman/Mailman/bin/nightly_gzip.py
===================================================================
--- trunk/mailman/Mailman/bin/nightly_gzip.py   2007-01-02 02:42:34 UTC (rev 
8130)
+++ trunk/mailman/Mailman/bin/nightly_gzip.py   2007-01-05 06:47:39 UTC (rev 
8131)
@@ -1,7 +1,5 @@
-#! @PYTHON@
+# Copyright (C) 1998-2007 by the Free Software Foundation, Inc.
 #
-# Copyright (C) 1998-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
@@ -31,6 +29,7 @@
 from Mailman import Version
 from Mailman.configuration import config
 from Mailman.i18n import _
+from Mailman.initialize import initialize
 
 __i18n_templates__ = True
 
@@ -77,7 +76,7 @@
 
 def main():
     opts, args, parser = parseargs()
-    config.load(opts.config)
+    initialize(opts.config)
 
     if config.ARCHIVE_TO_MBOX not in (1, 2) or config.GZIP_ARCHIVE_TXT_FILES:
         # We're only going to run the nightly archiver if messages are
@@ -119,12 +118,3 @@
                 files.append(txtfile)
         for f in files:
             compress(f, opts)
-
-
-
-if __name__ == '__main__':
-    omask = os.umask(002)
-    try:
-        main()
-    finally:
-        os.umask(omask)

Modified: trunk/mailman/Mailman/bin/update.py
===================================================================
--- trunk/mailman/Mailman/bin/update.py 2007-01-02 02:42:34 UTC (rev 8130)
+++ trunk/mailman/Mailman/bin/update.py 2007-01-05 06:47:39 UTC (rev 8131)
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2006 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2007 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
@@ -255,9 +255,7 @@
     o_html_dir = makeabs('public_html/archives/%s' % (listname))
     # Make the mbox directory if it's not there.
     if not os.path.exists(mbox_dir):
-        ou = os.umask(0)
-        os.mkdir(mbox_dir, 02775)
-        os.umask(ou)
+        Utils.makedirs(mbox_dir)
     else:
         # This shouldn't happen, but hey, just in case
         if not os.path.isdir(mbox_dir):
@@ -265,9 +263,7 @@
 For some reason, $mbox_dir exists as a file.  This won't work with b6, so I'm
 renaming it to ${mbox_dir}.tmp and proceeding.""")
             os.rename(mbox_dir, "%s.tmp" % (mbox_dir))
-            ou = os.umask(0)
-            os.mkdir(mbox_dir, 02775)
-            os.umask(ou)
+            Utils.makedirs(mbox_dir)
     # Move any existing mboxes around, but watch out for both a public and a
     # private one existing
     if os.path.isfile(o_pri_mbox_file) and os.path.isfile(o_pub_mbox_file):

Modified: trunk/mailman/Mailman/database/__init__.py
===================================================================
--- trunk/mailman/Mailman/database/__init__.py  2007-01-02 02:42:34 UTC (rev 
8130)
+++ trunk/mailman/Mailman/database/__init__.py  2007-01-05 06:47:39 UTC (rev 
8131)
@@ -1,4 +1,4 @@
-# Copyright (C) 2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2006-2007 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
@@ -22,12 +22,23 @@
 # from Mailman import database
 # database.add_list(foo)
 
+import os
 
+
 def initialize():
     from Mailman import database
+    from Mailman.LockFile import LockFile
+    from Mailman.configuration import config
     from Mailman.database.dbcontext import dbcontext
-
-    dbcontext.connect()
+    # Serialize this so we don't get multiple processes trying to create the
+    # database at the same time.
+    lockfile = os.path.join(config.LOCK_DIR, '<dbcreatelock>')
+    lock = LockFile(lockfile)
+    lock.lock()
+    try:
+        dbcontext.connect()
+    finally:
+        lock.unlock()
     for attr in dir(dbcontext):
         if attr.startswith('api_'):
             exposed_name = attr[4:]

Modified: trunk/mailman/Mailman/database/dbcontext.py
===================================================================
--- trunk/mailman/Mailman/database/dbcontext.py 2007-01-02 02:42:34 UTC (rev 
8130)
+++ trunk/mailman/Mailman/database/dbcontext.py 2007-01-05 06:47:39 UTC (rev 
8131)
@@ -1,4 +1,4 @@
-# Copyright (C) 2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2006-2007 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
@@ -15,10 +15,12 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
 # USA.
 
+import os
 import weakref
 
 from sqlalchemy import *
 from string import Template
+from urlparse import urlparse
 
 from Mailman import Version
 from Mailman.configuration import config
@@ -49,6 +51,21 @@
         # Calculate the engine url
         url = Template(config.SQLALCHEMY_ENGINE_URL).safe_substitute(
             config.paths)
+        # XXX By design of SQLite, database file creation does not honor
+        # umask.  See their ticket #1193:
+        # http://www.sqlite.org/cvstrac/tktview?tn=1193,31
+        #
+        # This sucks for us because the mailman.db file /must/ be group
+        # writable, however even though we guarantee our umask is 002 here, it
+        # still gets created without the necessary g+w permission, due to
+        # SQLite's policy.  This should only affect SQLite engines because its
+        # the only one that creates a little file on the local file system.
+        # This kludges around their bug by "touch"ing the database file before
+        # SQLite has any chance to create it, thus honoring the umask and
+        # ensuring the right permissions.  We only try to do this for SQLite
+        # engines, and yes, we could have chmod'd the file after the fact, but
+        # half dozen and all...
+        self._touch(url)
         self.metadata = BoundMetaData(url)
         self.metadata.engine.echo = config.SQLALCHEMY_ECHO
         # Create all the table objects, and then let SA conditionally create
@@ -74,6 +91,17 @@
             raise SchemaVersionMismatchError(row.version)
         self.session = create_session()
 
+    def _touch(self, url):
+        parts = urlparse(url)
+        # XXX Python 2.5; use parts.scheme and parts.path
+        if parts[0] <> 'sqlite':
+            return
+        path = os.path.normpath(parts[2])
+        fd = os.open(path, os.O_WRONLY |  os.O_NONBLOCK | os.O_CREAT, 0666)
+        # Ignore errors
+        if fd > 0:
+            os.close(fd)
+
     # Cooperative method for use with @txn decorator
     def _withtxn(self, meth, *args, **kws):
         try:

Modified: trunk/mailman/Mailman/initialize.py
===================================================================
--- trunk/mailman/Mailman/initialize.py 2007-01-02 02:42:34 UTC (rev 8130)
+++ trunk/mailman/Mailman/initialize.py 2007-01-05 06:47:39 UTC (rev 8131)
@@ -1,4 +1,4 @@
-# Copyright (C) 2006 by the Free Software Foundation, Inc.
+# Copyright (C) 2006-2007 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
@@ -24,6 +24,8 @@
 by the command line arguments.
 """
 
+import os
+
 import Mailman.configuration
 import Mailman.database
 import Mailman.loginit
@@ -31,6 +33,13 @@
 
 
 def initialize(config=None, propagate_logs=False):
+    # By default, set the umask so that only owner and group can read and
+    # write our files.  Specifically we must have g+rw and we probably want
+    # o-rwx although I think in most cases it doesn't hurt if other can read
+    # or write the files.  Note that the Pipermail archive has more
+    # restrictive permissions in order to handle private archives, but it
+    # handles that correctly.
+    os.umask(007)
     Mailman.configuration.config.load(config)
     Mailman.loginit.initialize(propagate_logs)
     Mailman.database.initialize()


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.
_______________________________________________
Mailman-checkins mailing list
[email protected]
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to