diff options
| author | bwarsaw | 2001-05-10 22:33:26 +0000 |
|---|---|---|
| committer | bwarsaw | 2001-05-10 22:33:26 +0000 |
| commit | 08eebcc9c4562bb2e9e7a9c879d6ba4c4ff3b719 (patch) | |
| tree | ecd5c3a549ee8ab625d586dd532d69c5ca046b6e /Mailman | |
| parent | 49f4bdb57d94c2e5d2986bfebe4457601645a907 (diff) | |
| download | mailman-08eebcc9c4562bb2e9e7a9c879d6ba4c4ff3b719.tar.gz mailman-08eebcc9c4562bb2e9e7a9c879d6ba4c4ff3b719.tar.zst mailman-08eebcc9c4562bb2e9e7a9c879d6ba4c4ff3b719.zip | |
List creation/removal hooks now keep both the plain text file
`aliases' and dbhash file `aliases.db' in sync. Specifically,
_addlist() => addlist()
_rmlist(): Removed.
addlist(): This now takes both a dbhash file and a file pointer to the
plain text file and adds the new list entries into both. If the plain
text file is empty (seek to end, tell() == 0), then an informative
comment is added to the top. In the plain text file, `stanza' marker
comments are used around the list alias entries. A trailing blank
line is always added after the stanza.
create(): This is now just a wrapper for addlist() (since addlist() is
now used by bin/genaliases).
remove(): Does the bulk of what _rmlist() used to do, plus it keeps
the plain text file in sync. It searches for the appropriate stanza
marker and removes every line between it and the stanza end marker.
(Actually, it does this by copying to a tmp file and shuffling the tmp
file to the real file.)
Diffstat (limited to 'Mailman')
| -rw-r--r-- | Mailman/MTA/Postfix.py | 149 |
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) |
