summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Mailman/MTA/Postfix.py149
1 files changed, 120 insertions, 29 deletions
diff --git a/Mailman/MTA/Postfix.py b/Mailman/MTA/Postfix.py
index 5a993d46d..7578435c5 100644
--- a/Mailman/MTA/Postfix.py
+++ b/Mailman/MTA/Postfix.py
@@ -25,59 +25,150 @@ import time
import dbhash
import errno
import pwd
-import grp
from stat import *
from Mailman import mm_cfg
+from Mailman import LockFile
from Mailman.i18n import _
+from Mailman.MTA.Utils import makealiases
-DBFILE = os.path.join(mm_cfg.DATA_DIR, 'aliases.db')
+TEXTFILE = os.path.join(mm_cfg.DATA_DIR, 'aliases')
+DBFILE = TEXTFILE + '.db'
+LOCKFILE = os.path.join(mm_cfg.LOCK_DIR, 'creator')
-def _addlist(listname, db):
- wrapper = os.path.join(mm_cfg.WRAPPER_DIR, 'wrapper')
- # Every key and value in the dbhash file as created by Postfix must end in
- # a null byte. That is, except YP_LAST_MODIFIED and YP_MASTER_NAME.
- db[listname + '\0'] = '"|%s post %s"\0' % (wrapper, listname)
- db[listname + '-admin\0'] = '"|%s mailowner %s"\0' % (wrapper, listname)
- db[listname + '-owner\0'] = '%s-admin\0' % listname
- db[listname + '-request\0'] = '"|%s mailcmd %s"\0' % (wrapper, listname)
- # Always update YP_LAST_MODIFIED
- db['YP_LAST_MODIFIED'] = '%010d' % time.time()
- # Add a YP_MASTER_NAME only if there isn't one already
- if not db.has_key('YP_MASTER_NAME'):
- db['YP_MASTER_NAME'] = socket.getfqdn()
+def makelock():
+ return LockFile.LockFile(LOCKFILE)
-def _rmlist(listname, db):
- for extra in ('', '-admin', '-owner', '-request'):
- try:
- del db[listname + extra + '\0']
- except KeyError:
- pass
+def addlist(mlist, db, fp):
+ listname = mlist.internal_name()
+ fieldsz = len(listname) + len('-request')
+ # Seek to the end of the file, but if it's empty write the standard
+ # disclaimer.
+ 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 aliases.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.
+"""
+ # The text file entries get a little extra info
+ print >> fp, '# STANZA START:', listname
+ print >> fp, '# CREATED:', time.ctime()
+ # Now add all the standard alias entries
+ for k, v in makealiases(listname):
+ # Every key and value in the dbhash file as created by Postfix
+ # must end in a null byte. That is, except YP_LAST_MODIFIED and
+ # YP_MASTER_NAME.
+ db[k + '\0'] = v + '\0'
+ # Format the text file nicely
+ print >> fp, k + ':', ((fieldsz - len(k)) * ' '), v
# Always update YP_LAST_MODIFIED
db['YP_LAST_MODIFIED'] = '%010d' % time.time()
# Add a YP_MASTER_NAME only if there isn't one already
if not db.has_key('YP_MASTER_NAME'):
db['YP_MASTER_NAME'] = socket.getfqdn()
+ # Finish the text file stanza
+ print >> fp, '# STANZA END:', listname
+ print >> fp
def create(mlist):
- listname = mlist.internal_name()
- db = dbhash.open(DBFILE, 'c')
- _addlist(listname, db)
- db.sync()
+ # Acquire the global list database lock
+ lock = makelock()
+ lock.lock()
+ try:
+ # Crack open the dbhash file
+ db = dbhash.open(DBFILE, 'c')
+ # Crack open the plain text file
+ try:
+ 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)
+
+ addlist(mlist, db, fp)
+ # And flush everything out to disk
+ db.sync()
+ fp.close()
+ finally:
+ lock.unlock(unconditionally=1)
def remove(mlist):
- listname = mlist.internal_name()
- db = dbhash.open(DBFILE, 'c')
- _rmlist(listname, db)
- db.sync()
+ # Acquire the global list database lock
+ lock = LockFile.LockFile(LOCKFILE)
+ lock.lock()
+ try:
+ listname = mlist.internal_name()
+ # Crack open the dbhash file, and delete all the entries
+ db = dbhash.open(DBFILE, 'c')
+ for k, v in makealiases(listname):
+ try:
+ del db[k + '\0']
+ except KeyError:
+ pass
+ # Always update YP_LAST_MODIFIED
+ db['YP_LAST_MODIFIED'] = '%010d' % time.time()
+ # Add a YP_MASTER_NAME only if there isn't one already
+ if not db.has_key('YP_MASTER_NAME'):
+ db['YP_MASTER_NAME'] = socket.getfqdn()
+ # And flush the changes to disk
+ db.sync()
+ # Now do our best to filter out the proper stanza from the text file.
+ # The text file better exist!
+ outfile = TEXTFILE
+ try:
+ infp = open(TEXTFILE)
+ except IOError, e:
+ if e.errno <> errno.ENOENT: raise
+ # Otherwise, there's no text file to filter so we're done.
+ return
+ omask = os.umask(007)
+ try:
+ outfp = open(TEXTFILE + '.tmp', 'w')
+ finally:
+ os.umask(omask)
+ filteroutp = 0
+ start = '# STANZA START: ' + listname
+ end = '# STANZA END: ' + listname
+ while 1:
+ line = infp.readline()
+ if not line:
+ break
+ # If we're filtering out a stanza, just look for the end marker
+ # and filter out everything in between. If we're not in the
+ # middle of filter out a stanza, we're just looking for the proper
+ # begin marker.
+ if filteroutp:
+ if line.startswith(end):
+ filteroutp = 0
+ # Discard the trailing blank line, but don't worry if
+ # we're at the end of the file.
+ infp.readline()
+ # Otherwise, ignore the line
+ else:
+ if line.startswith(start):
+ # Filter out this stanza
+ filteroutp = 1
+ else:
+ outfp.write(line)
+ # Close up shop, and rotate the files
+ infp.close()
+ outfp.close()
+ os.rename(TEXTFILE+'.tmp', TEXTFILE)
+ finally:
+ lock.unlock(unconditionally=1)