summaryrefslogtreecommitdiff
path: root/mailman/mta/Postfix.py
diff options
context:
space:
mode:
Diffstat (limited to 'mailman/mta/Postfix.py')
-rw-r--r--mailman/mta/Postfix.py411
1 files changed, 0 insertions, 411 deletions
diff --git a/mailman/mta/Postfix.py b/mailman/mta/Postfix.py
deleted file mode 100644
index 901c21089..000000000
--- a/mailman/mta/Postfix.py
+++ /dev/null
@@ -1,411 +0,0 @@
-# Copyright (C) 2001-2009 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""Creation/deletion hooks for the Postfix MTA."""
-
-import os
-import grp
-import pwd
-import time
-import errno
-import logging
-
-from locknix.lockfile import Lock
-from stat import *
-
-from mailman import Utils
-from mailman.MTA.Utils import makealiases
-from mailman.configuration import config
-from mailman.i18n import _
-
-LOCKFILE = os.path.join(config.LOCK_DIR, 'creator')
-ALIASFILE = os.path.join(config.DATA_DIR, 'aliases')
-VIRTFILE = os.path.join(config.DATA_DIR, 'virtual-mailman')
-TRPTFILE = os.path.join(config.DATA_DIR, 'transport')
-
-log = logging.getLogger('mailman.error')
-
-
-
-def _update_maps():
- msg = 'command failed: %s (status: %s, %s)'
- if config.USE_LMTP:
- tcmd = config.POSTFIX_MAP_CMD + ' ' + TRPTFILE
- status = (os.system(tcmd) >> 8) & 0xff
- if status:
- errstr = os.strerror(status)
- log.error(msg, tcmd, status, errstr)
- raise RuntimeError(msg % (tcmd, status, errstr))
- acmd = config.POSTFIX_ALIAS_CMD + ' ' + ALIASFILE
- status = (os.system(acmd) >> 8) & 0xff
- if status:
- errstr = os.strerror(status)
- log.error(msg, acmd, status, errstr)
- raise RuntimeError(msg % (acmd, status, errstr))
- if os.path.exists(VIRTFILE):
- vcmd = config.POSTFIX_MAP_CMD + ' ' + VIRTFILE
- status = (os.system(vcmd) >> 8) & 0xff
- if status:
- errstr = os.strerror(status)
- log.error(msg, vcmd, status, errstr)
- raise RuntimeError(msg % (vcmd, status, errstr))
-
-
-
-def _zapfile(filename):
- # Truncate the file w/o messing with the file permissions, but only if it
- # already exists.
- if os.path.exists(filename):
- fp = open(filename, 'w')
- fp.close()
-
-
-def clear():
- _zapfile(ALIASFILE)
- _zapfile(VIRTFILE)
- _zapfile(TRPTFILE)
-
-
-
-def _addlist(mlist, fp):
- # Set up the mailman-loop address
- loopaddr = Utils.ParseEmail(Utils.get_site_noreply())[0]
- loopmbox = os.path.join(config.DATA_DIR, 'owner-bounces.mbox')
- # 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 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.
-"""
- print >> fp, '# The ultimate loop stopper address'
- print >> fp, '%s: %s' % (loopaddr, loopmbox)
- print >> fp
- # Bootstrapping. bin/genaliases must be run before any lists are created,
- # but if no lists exist yet then mlist is None. The whole point of the
- # exercise is to get the minimal aliases.db file into existance.
- if mlist is None:
- return
- listname = mlist.internal_name()
- hostname = mlist.host_name
- fieldsz = len(listname) + len('-unsubscribe')
- # The text file entries get a little extra info
- print >> fp, '# STANZA START: %s@%s' % (listname, hostname)
- print >> fp, '# CREATED:', time.ctime(time.time())
- # Now add all the standard alias entries
- for k, v in makealiases(mlist):
- l = len(k)
- if hostname in config.POSTFIX_STYLE_VIRTUAL_DOMAINS:
- k += config.POSTFIX_VIRTUAL_SEPARATOR + hostname
- # Format the text file nicely
- print >> fp, k + ':', ((fieldsz - l) * ' ') + v
- # Finish the text file stanza
- print >> fp, '# STANZA END: %s@%s' % (listname, hostname)
- print >> 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 = mlist.no_reply_address
- 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 virtual-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.
-#
-# Note that you should already have this virtual domain set up properly in
-# your Postfix installation. See README.POSTFIX for details.
-
-# LOOP ADDRESSES START
-%s\t%s
-# LOOP ADDRESSES END
-""" % (loopaddr, loopdest)
- # The text file entries get a little extra info
- print >> fp, '# STANZA START: %s@%s' % (listname, hostname)
- print >> fp, '# CREATED:', time.ctime(time.time())
- # Now add all the standard alias entries
- for k, v in makealiases(mlist):
- fqdnaddr = '%s@%s' % (k, hostname)
- l = len(k)
- # Format the text file nicely
- if hostname in config.POSTFIX_STYLE_VIRTUAL_DOMAINS:
- k += config.POSTFIX_VIRTUAL_SEPARATOR + hostname
- print >> fp, fqdnaddr, ((fieldsz - l) * ' '), k
- # Finish the text file stanza
- print >> fp, '# STANZA END: %s@%s' % (listname, hostname)
- print >> fp
-
-
-
-# Blech.
-def _check_for_virtual_loopaddr(mlist, filename, func):
- loopaddr = mlist.no_reply_address
- loopdest = Utils.ParseEmail(loopaddr)[0]
- if func is _addtransport:
- loopdest = 'local:' + loopdest
- infp = open(filename)
- outfp = open(filename + '.tmp', 'w')
- try:
- # Find the start of the loop address block
- while True:
- line = infp.readline()
- if not line:
- break
- outfp.write(line)
- if line.startswith('# LOOP ADDRESSES START'):
- break
- # Now see if our domain has already been written
- while True:
- line = infp.readline()
- if not line:
- break
- if line.startswith('# LOOP ADDRESSES END'):
- # It hasn't
- print >> outfp, '%s\t%s' % (loopaddr, loopdest)
- outfp.write(line)
- break
- elif line.startswith(loopaddr):
- # We just found it
- outfp.write(line)
- break
- else:
- # This isn't our loop address, so spit it out and continue
- outfp.write(line)
- outfp.writelines(infp.readlines())
- finally:
- infp.close()
- outfp.close()
- os.rename(filename + '.tmp', filename)
-
-
-
-def _addtransport(mlist, fp):
- # Set up the mailman-loop address
- loopaddr = mlist.no_reply_address
- loopdest = Utils.ParseEmail(loopaddr)[0]
- # create/add postfix transport file for mailman
- 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.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.
-
-# LOOP ADDRESSES START
-%s\tlocal:%s
-# LOOP ADDRESSES END
-""" % (loopaddr, loopdest)
- # List LMTP_ONLY_DOMAINS
- if config.LMTP_ONLY_DOMAINS:
- print >> fp, '# LMTP ONLY DOMAINS START'
- for dom in config.LMTP_ONLY_DOMAINS:
- print >> fp, '%s\tlmtp:%s:%s' % (dom,
- config.LMTP_HOST,
- config.LMTP_PORT)
- print >> fp, '# LMTP ONLY DOMAINS END\n'
- listname = mlist.internal_name()
- hostname = mlist.host_name
- # No need of individual local part if the domain is LMTP only
- if hostname in config.LMTP_ONLY_DOMAINS:
- return
- fieldsz = len(listname) + len(hostname) + len('-unsubscribe') + 1
- # The text file entries get a little extra info
- print >> fp, '# STANZA START: %s@%s' % (listname, hostname)
- print >> fp, '# CREATED:', time.ctime(time.time())
- # Now add transport entries
- for k, v in makealiases(mlist):
- l = len(k + hostname) + 1
- print >> fp, '%s@%s' % (k, hostname), ((fieldsz - l) * ' ')\
- + 'lmtp:%s:%s' % (config.LMTP_HOST, config.LMTP_PORT)
- #
- print >> fp, '# STANZA END: %s@%s' % (listname, hostname)
- print >> fp
-
-
-
-def _do_create(mlist, textfile, func):
- # Crack open the plain text file
- try:
- fp = open(textfile, 'r+')
- except IOError, e:
- if e.errno <> errno.ENOENT:
- raise
- fp = open(textfile, 'w+')
- try:
- func(mlist, fp)
- finally:
- fp.close()
- # Now double check the virtual plain text file
- if func in (_addvirtual, _addtransport):
- _check_for_virtual_loopaddr(mlist, textfile, func)
-
-
-def create(mlist, cgi=False, nolock=False, quiet=False):
- # Acquire the global list database lock. quiet flag is ignored.
- lock = None
- if not nolock:
- # XXX FIXME
- lock = makelock()
- lock.lock()
- # Do the aliases file, which always needs to be done
- try:
- if config.USE_LMTP:
- _do_create(mlist, TRPTFILE, _addtransport)
- _do_create(None, ALIASFILE, _addlist)
- else:
- _do_create(mlist, ALIASFILE, _addlist)
- if mlist.host_name in config.POSTFIX_STYLE_VIRTUAL_DOMAINS:
- _do_create(mlist, VIRTFILE, _addvirtual)
- _update_maps()
- finally:
- if lock:
- lock.unlock(unconditionally=True)
-
-
-
-def _do_remove(mlist, textfile):
- listname = mlist.internal_name()
- hostname = mlist.host_name
- # Now do our best to filter out the proper stanza from the text file.
- # The text file better exist!
- outfp = None
- 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
- try:
- outfp = open(textfile + '.tmp', 'w')
- filteroutp = False
- start = '# STANZA START: %s@%s' % (listname, hostname)
- end = '# STANZA END: %s@%s' % (listname, hostname)
- 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
- # filtering out a stanza, we're just looking for the proper begin
- # marker.
- if filteroutp:
- if line.strip() == end:
- filteroutp = False
- # 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.strip() == start:
- # Filter out this stanza
- filteroutp = True
- else:
- outfp.write(line)
- # Close up shop, and rotate the files
- finally:
- infp.close()
- outfp.close()
- os.rename(textfile+'.tmp', textfile)
-
-
-def remove(mlist, cgi=False):
- # Acquire the global list database lock
- with Lock(LOCKFILE):
- if config.USE_LMTP:
- _do_remove(mlist, TRPTFILE)
- else:
- _do_remove(mlist, ALIASFILE)
- if mlist.host_name in config.POSTFIX_STYLE_VIRTUAL_DOMAINS:
- _do_remove(mlist, VIRTFILE)
- # Regenerate the alias and map files
- _update_maps()
- config.db.commit()
-
-
-
-def checkperms(state):
- targetmode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
- for file in ALIASFILE, VIRTFILE, TRPTFILE:
- if state.VERBOSE:
- print _('checking permissions on %(file)s')
- stat = None
- try:
- stat = os.stat(file)
- except OSError, e:
- if e.errno <> errno.ENOENT:
- raise
- if stat and (stat[ST_MODE] & targetmode) <> targetmode:
- state.ERRORS += 1
- octmode = oct(stat[ST_MODE])
- print _('%(file)s permissions must be 066x (got %(octmode)s)'),
- if state.FIX:
- print _('(fixing)')
- os.chmod(file, stat[ST_MODE] | targetmode)
- else:
- print
- # Make sure the corresponding .db files are owned by the Mailman user.
- # We don't need to check the group ownership of the file, since
- # check_perms checks this itself.
- dbfile = file + '.db'
- stat = None
- try:
- stat = os.stat(dbfile)
- except OSError, e:
- if e.errno <> errno.ENOENT:
- raise
- continue
- if state.VERBOSE:
- print _('checking ownership of %(dbfile)s')
- user = config.MAILMAN_USER
- ownerok = stat[ST_UID] == pwd.getpwnam(user)[2]
- if not ownerok:
- try:
- owner = pwd.getpwuid(stat[ST_UID])[0]
- except KeyError:
- owner = 'uid %d' % stat[ST_UID]
- print _('%(dbfile)s owned by %(owner)s (must be owned by %(user)s'),
- state.ERRORS += 1
- if state.FIX:
- print _('(fixing)')
- uid = pwd.getpwnam(user)[2]
- gid = grp.getgrnam(config.MAILMAN_GROUP)[2]
- os.chown(dbfile, uid, gid)
- else:
- print
- if stat and (stat[ST_MODE] & targetmode) <> targetmode:
- state.ERRORS += 1
- octmode = oct(stat[ST_MODE])
- print _('%(dbfile)s permissions must be 066x (got %(octmode)s)'),
- if state.FIX:
- print _('(fixing)')
- os.chmod(dbfile, stat[ST_MODE] | targetmode)
- else:
- print