summaryrefslogtreecommitdiff
path: root/mailman/bin
diff options
context:
space:
mode:
Diffstat (limited to 'mailman/bin')
-rw-r--r--mailman/bin/__init__.py61
-rw-r--r--mailman/bin/add_members.py186
-rw-r--r--mailman/bin/arch.py151
-rw-r--r--mailman/bin/bumpdigests.py74
-rw-r--r--mailman/bin/check_perms.py408
-rw-r--r--mailman/bin/checkdbs.py199
-rw-r--r--mailman/bin/cleanarch.py133
-rw-r--r--mailman/bin/config_list.py332
-rw-r--r--mailman/bin/create_list.py129
-rw-r--r--mailman/bin/disabled.py201
-rw-r--r--mailman/bin/docs/master.txt49
-rw-r--r--mailman/bin/dumpdb.py88
-rw-r--r--mailman/bin/export.py310
-rw-r--r--mailman/bin/find_member.py135
-rw-r--r--mailman/bin/gate_news.py243
-rw-r--r--mailman/bin/genaliases.py64
-rw-r--r--mailman/bin/import.py315
-rw-r--r--mailman/bin/inject.py89
-rw-r--r--mailman/bin/list_lists.py104
-rw-r--r--mailman/bin/list_members.py201
-rw-r--r--mailman/bin/list_owners.py88
-rw-r--r--mailman/bin/mailmanctl.py232
-rw-r--r--mailman/bin/master.py452
-rw-r--r--mailman/bin/mmsitepass.py113
-rw-r--r--mailman/bin/nightly_gzip.py117
-rw-r--r--mailman/bin/qrunner.py269
-rw-r--r--mailman/bin/remove_list.py83
-rw-r--r--mailman/bin/senddigests.py83
-rw-r--r--mailman/bin/set_members.py189
-rw-r--r--mailman/bin/show_config.py97
-rw-r--r--mailman/bin/show_qfiles.py91
-rw-r--r--mailman/bin/unshunt.py51
-rw-r--r--mailman/bin/update.py660
-rw-r--r--mailman/bin/version.py46
-rw-r--r--mailman/bin/withlist.py220
35 files changed, 0 insertions, 6263 deletions
diff --git a/mailman/bin/__init__.py b/mailman/bin/__init__.py
deleted file mode 100644
index d61693c5e..000000000
--- a/mailman/bin/__init__.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# Copyright (C) 2007-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/>.
-
-__all__ = [
- 'add_members',
- 'arch',
- 'bounces',
- 'bumpdigests',
- 'check_perms',
- 'checkdbs',
- 'cleanarch',
- 'config_list',
- 'confirm',
- 'create_list',
- 'disabled',
- 'dumpdb',
- 'export',
- 'find_member',
- 'gate_news',
- 'genaliases',
- 'import',
- 'inject',
- 'join',
- 'leave',
- 'list_lists',
- 'list_members',
- 'list_owners',
- 'mailmanctl',
- 'make_instance',
- 'master',
- 'mmsitepass',
- 'nightly_gzip',
- 'owner',
- 'post',
- 'qrunner',
- 'remove_list',
- 'request',
- 'senddigests',
- 'set_members',
- 'show_config',
- 'show_qfiles',
- 'testall',
- 'unshunt',
- 'update',
- 'version',
- 'withlist',
- ]
diff --git a/mailman/bin/add_members.py b/mailman/bin/add_members.py
deleted file mode 100644
index 9c87f4af9..000000000
--- a/mailman/bin/add_members.py
+++ /dev/null
@@ -1,186 +0,0 @@
-# Copyright (C) 1998-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/>.
-
-import os
-import sys
-import codecs
-
-from cStringIO import StringIO
-from email.utils import parseaddr
-
-from mailman import Message
-from mailman import Utils
-from mailman import i18n
-from mailman.app.membership import add_member
-from mailman.config import config
-from mailman.core import errors
-from mailman.interfaces.member import AlreadySubscribedError, DeliveryMode
-from mailman.options import SingleMailingListOptions
-
-_ = i18n._
-
-
-
-class ScriptOptions(SingleMailingListOptions):
- usage=_("""\
-%prog [options]
-
-Add members to a list. 'listname' is the name of the Mailman list you are
-adding members to; the list must already exist.
-
-You must supply at least one of -r and -d options. At most one of the
-files can be '-'.
-""")
-
- def add_options(self):
- super(ScriptOptions, self).add_options()
- self.parser.add_option(
- '-r', '--regular-members-file',
- type='string', dest='regular', help=_("""\
-A file containing addresses of the members to be added, one address per line.
-This list of people become non-digest members. If file is '-', read addresses
-from stdin."""))
- self.parser.add_option(
- '-d', '--digest-members-file',
- type='string', dest='digest', help=_("""\
-Similar to -r, but these people become digest members."""))
- self.parser.add_option(
- '-w', '--welcome-msg',
- type='yesno', metavar='<y|n>', help=_("""\
-Set whether or not to send the list members a welcome message, overriding
-whatever the list's 'send_welcome_msg' setting is."""))
- self.parser.add_option(
- '-a', '--admin-notify',
- type='yesno', metavar='<y|n>', help=_("""\
-Set whether or not to send the list administrators a notification on the
-success/failure of these subscriptions, overriding whatever the list's
-'admin_notify_mchanges' setting is."""))
-
- def sanity_check(self):
- if not self.options.listname:
- self.parser.error(_('Missing listname'))
- if len(self.arguments) > 0:
- self.parser.print_error(_('Unexpected arguments'))
- if self.options.regular is None and self.options.digest is None:
- parser.error(_('At least one of -r or -d is required'))
- if self.options.regular == '-' and self.options.digest == '-':
- parser.error(_("-r and -d cannot both be '-'"))
-
-
-
-def readfile(filename):
- if filename == '-':
- fp = sys.stdin
- else:
- # XXX Need to specify other encodings.
- fp = codecs.open(filename, encoding='utf-8')
- # Strip all the lines of whitespace and discard blank lines
- try:
- return set(line.strip() for line in fp if line)
- finally:
- if fp is not sys.stdin:
- fp.close()
-
-
-
-class Tee:
- def __init__(self, outfp):
- self._outfp = outfp
-
- def write(self, msg):
- sys.stdout.write(msg)
- self._outfp.write(msg)
-
-
-
-def addall(mlist, subscribers, delivery_mode, ack, admin_notify, outfp):
- tee = Tee(outfp)
- for subscriber in subscribers:
- try:
- fullname, address = parseaddr(subscriber)
- # Watch out for the empty 8-bit string.
- if not fullname:
- fullname = u''
- password = Utils.MakeRandomPassword()
- add_member(mlist, address, fullname, password, delivery_mode,
- unicode(config.mailman.default_language))
- # XXX Support ack and admin_notify
- except AlreadySubscribedError:
- print >> tee, _('Already a member: $subscriber')
- except errors.InvalidEmailAddress:
- if not address:
- print >> tee, _('Bad/Invalid email address: blank line')
- else:
- print >> tee, _('Bad/Invalid email address: $subscriber')
- else:
- print >> tee, _('Subscribing: $subscriber')
-
-
-
-def main():
- options = ScriptOptions()
- options.initialize()
-
- fqdn_listname = options.options.listname
- mlist = config.db.list_manager.get(fqdn_listname)
- if mlist is None:
- parser.error(_('No such list: $fqdn_listname'))
-
- # Set up defaults.
- send_welcome_msg = (options.options.welcome_msg
- if options.options.welcome_msg is not None
- else mlist.send_welcome_msg)
- admin_notify = (options.options.admin_notify
- if options.options.admin_notify is not None
- else mlist.admin_notify)
-
- with i18n.using_language(mlist.preferred_language):
- if options.options.digest:
- dmembers = readfile(options.options.digest)
- else:
- dmembers = set()
- if options.options.regular:
- nmembers = readfile(options.options.regular)
- else:
- nmembers = set()
-
- if not dmembers and not nmembers:
- print _('Nothing to do.')
- sys.exit(0)
-
- outfp = StringIO()
- if nmembers:
- addall(mlist, nmembers, DeliveryMode.regular,
- send_welcome_msg, admin_notify, outfp)
-
- if dmembers:
- addall(mlist, dmembers, DeliveryMode.mime_digests,
- send_welcome_msg, admin_notify, outfp)
-
- config.db.commit()
-
- if admin_notify:
- subject = _('$mlist.real_name subscription notification')
- msg = Message.UserNotification(
- mlist.owner, mlist.no_reply_address, subject,
- outfp.getvalue(), mlist.preferred_language)
- msg.send(mlist)
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/arch.py b/mailman/bin/arch.py
deleted file mode 100644
index a27fa8d7f..000000000
--- a/mailman/bin/arch.py
+++ /dev/null
@@ -1,151 +0,0 @@
-# Copyright (C) 1998-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/>.
-
-import os
-import sys
-import errno
-import shutil
-import optparse
-
-from locknix.lockfile import Lock
-
-from mailman import i18n
-from mailman.Archiver.HyperArch import HyperArchive
-from mailman.Defaults import hours
-from mailman.configuration import config
-from mailman.initialize import initialize
-from mailman.version import MAILMAN_VERSION
-
-_ = i18n._
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-%%prog [options] listname [mbox]
-
-Rebuild a list's archive.
-
-Use this command to rebuild the archives for a mailing list. You may want to
-do this if you edit some messages in an archive, or remove some messages from
-an archive.
-
-Where 'mbox' is the path to a list's complete mbox archive. Usually this will
-be some path in the archives/private directory. For example:
-
-% bin/arch mylist archives/private/mylist.mbox/mylist.mbox
-
-'mbox' is optional. If it is missing, it is calculated from the listname.
-"""))
- parser.add_option('-q', '--quiet',
- dest='verbose', default=True, action='store_false',
- help=_('Make the archiver output less verbose'))
- parser.add_option('--wipe',
- default=False, action='store_true',
- help=_("""\
-First wipe out the original archive before regenerating. You usually want to
-specify this argument unless you're generating the archive in chunks."""))
- parser.add_option('-s', '--start',
- default=None, type='int', metavar='N',
- help=_("""\
-Start indexing at article N, where article 0 is the first in the mbox.
-Defaults to 0."""))
- parser.add_option('-e', '--end',
- default=None, type='int', metavar='M',
- help=_("""\
-End indexing at article M. This script is not very efficient with respect to
-memory management, and for large archives, it may not be possible to index the
-mbox entirely. For that reason, you can specify the start and end article
-numbers."""))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- if len(args) < 1:
- parser.print_help()
- print >> sys.stderr, _('listname is required')
- sys.exit(1)
- if len(args) > 2:
- parser.print_help()
- print >> sys.stderr, _('Unexpected arguments')
- sys.exit(1)
- return parser, opts, args
-
-
-
-def main():
- parser, opts, args = parseargs()
- initialize(opts.config)
-
- i18n.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
- listname = args[0].lower().strip()
- if len(args) < 2:
- mbox = None
- else:
- mbox = args[1]
-
- # Open the mailing list object
- mlist = config.list_manager.get(listname)
- if mlist is None:
- parser.error(_('No such list: $listname'))
- if mbox is None:
- mbox = mlist.ArchiveFileName()
-
- i18n.set_language(mlist.preferred_language)
- # Lay claim to the archive's lock file. This is so no other post can
- # mess up the archive while we're processing it. Try to pick a
- # suitably long period of time for the lock lifetime even though we
- # really don't know how long it will take.
- #
- # XXX processUnixMailbox() should refresh the lock.
- lock_path = os.path.join(mlist.data_path, '.archiver.lck')
- with Lock(lock_path, lifetime=int(hours(3))):
- # Try to open mbox before wiping old archive.
- try:
- fp = open(mbox)
- except IOError, e:
- if e.errno == errno.ENOENT:
- print >> sys.stderr, _('Cannot open mbox file: $mbox')
- else:
- print >> sys.stderr, e
- sys.exit(1)
- # Maybe wipe the old archives
- if opts.wipe:
- if mlist.scrub_nondigest:
- # TK: save the attachments dir because they are not in mbox
- saved = False
- atchdir = os.path.join(mlist.archive_dir(), 'attachments')
- savedir = os.path.join(mlist.archive_dir() + '.mbox',
- 'attachments')
- try:
- os.rename(atchdir, savedir)
- saved = True
- except OSError, e:
- if e.errno <> errno.ENOENT:
- raise
- shutil.rmtree(mlist.archive_dir())
- if mlist.scrub_nondigest and saved:
- os.renames(savedir, atchdir)
-
- archiver = HyperArchive(mlist)
- archiver.VERBOSE = opts.verbose
- try:
- archiver.processUnixMailbox(fp, opts.start, opts.end)
- finally:
- archiver.close()
- fp.close()
diff --git a/mailman/bin/bumpdigests.py b/mailman/bin/bumpdigests.py
deleted file mode 100644
index b1ed37a21..000000000
--- a/mailman/bin/bumpdigests.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# Copyright (C) 1998-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/>.
-
-import sys
-import optparse
-
-from mailman import errors
-from mailman import MailList
-from mailman.configuration import config
-from mailman.i18n import _
-from mailman.version import MAILMAN_VERSION
-
-# Work around known problems with some RedHat cron daemons
-import signal
-signal.signal(signal.SIGCHLD, signal.SIG_DFL)
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-%prog [options] [listname ...]
-
-Increment the digest volume number and reset the digest number to one. All
-the lists named on the command line are bumped. If no list names are given,
-all lists are bumped."""))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- return opts, args, parser
-
-
-
-def main():
- opts, args, parser = parseargs()
- config.load(opts.config)
-
- listnames = set(args or config.list_manager.names)
- if not listnames:
- print _('Nothing to do.')
- sys.exit(0)
-
- for listname in listnames:
- try:
- # Be sure the list is locked
- mlist = MailList.MailList(listname)
- except errors.MMListError, e:
- parser.print_help()
- print >> sys.stderr, _('No such list: $listname')
- sys.exit(1)
- try:
- mlist.bump_digest_volume()
- finally:
- mlist.Save()
- mlist.Unlock()
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/check_perms.py b/mailman/bin/check_perms.py
deleted file mode 100644
index 4b75aa9f6..000000000
--- a/mailman/bin/check_perms.py
+++ /dev/null
@@ -1,408 +0,0 @@
-# Copyright (C) 1998-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/>.
-
-import os
-import sys
-import pwd
-import grp
-import errno
-import optparse
-
-from stat import *
-
-from mailman.configuration import config
-from mailman.i18n import _
-from mailman.version import MAILMAN_VERSION
-
-
-# XXX Need to check the archives/private/*/database/* files
-
-
-
-class State:
- FIX = False
- VERBOSE = False
- ERRORS = 0
-
-STATE = State()
-
-DIRPERMS = S_ISGID | S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH
-QFILEPERMS = S_ISGID | S_IRWXU | S_IRWXG
-PYFILEPERMS = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
-ARTICLEFILEPERMS = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
-MBOXPERMS = S_IRGRP | S_IWGRP | S_IRUSR | S_IWUSR
-PRIVATEPERMS = QFILEPERMS
-
-
-
-def statmode(path):
- return os.stat(path).st_mode
-
-
-def statgidmode(path):
- stat = os.stat(path)
- return stat.st_mode, stat.st_gid
-
-
-seen = {}
-
-# libc's getgrgid re-opens /etc/group each time :(
-_gidcache = {}
-
-def getgrgid(gid):
- data = _gidcache.get(gid)
- if data is None:
- data = grp.getgrgid(gid)
- _gidcache[gid] = data
- return data
-
-
-
-def checkwalk(arg, dirname, names):
- # Short-circuit duplicates
- if seen.has_key(dirname):
- return
- seen[dirname] = True
- for name in names:
- path = os.path.join(dirname, name)
- if arg.VERBOSE:
- print _(' checking gid and mode for $path')
- try:
- mode, gid = statgidmode(path)
- except OSError, e:
- if e.errno <> errno.ENOENT: raise
- continue
- if gid <> MAILMAN_GID:
- try:
- groupname = getgrgid(gid)[0]
- except KeyError:
- groupname = '<anon gid %d>' % gid
- arg.ERRORS += 1
- print _(
- '$path bad group (has: $groupname, expected $MAILMAN_GROUP)'),
- if STATE.FIX:
- print _('(fixing)')
- os.chown(path, -1, MAILMAN_GID)
- else:
- print
- # Most directories must be at least rwxrwsr-x.
- # The private archive directory and database directory must be at
- # least rwxrws---. Their 'other' permissions are checked in
- # checkarchives() and checkarchivedbs() below. Their 'user' and
- # 'group' permissions are checked here.
- # The directories under qfiles should be rwxrws---. Their 'user' and
- # 'group' permissions are checked here. Their 'other' permissions
- # aren't checked.
- private = config.PRIVATE_ARCHIVE_FILE_DIR
- if path == private or (
- os.path.commonprefix((path, private)) == private
- and os.path.split(path)[1] == 'database'):
- # then...
- targetperms = PRIVATEPERMS
- elif (os.path.commonprefix((path, config.QUEUE_DIR))
- == config.QUEUE_DIR):
- targetperms = QFILEPERMS
- else:
- targetperms = DIRPERMS
- octperms = oct(targetperms)
- if S_ISDIR(mode) and (mode & targetperms) <> targetperms:
- arg.ERRORS += 1
- print _('directory permissions must be $octperms: $path'),
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(path, mode | targetperms)
- else:
- print
- elif os.path.splitext(path)[1] in ('.py', '.pyc', '.pyo'):
- octperms = oct(PYFILEPERMS)
- if mode & PYFILEPERMS <> PYFILEPERMS:
- print _('source perms must be $octperms: $path'),
- arg.ERRORS += 1
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(path, mode | PYFILEPERMS)
- else:
- print
- elif path.endswith('-article'):
- # Article files must be group writeable
- octperms = oct(ARTICLEFILEPERMS)
- if mode & ARTICLEFILEPERMS <> ARTICLEFILEPERMS:
- print _('article db files must be $octperms: $path'),
- arg.ERRORS += 1
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(path, mode | ARTICLEFILEPERMS)
- else:
- print
-
-
-
-def checkall():
- # first check PREFIX
- if STATE.VERBOSE:
- prefix = config.PREFIX
- print _('checking mode for $prefix')
- dirs = {}
- for d in (config.PREFIX, config.EXEC_PREFIX, config.VAR_PREFIX,
- config.LOG_DIR):
- dirs[d] = True
- for d in dirs.keys():
- try:
- mode = statmode(d)
- except OSError, e:
- if e.errno <> errno.ENOENT: raise
- print _('WARNING: directory does not exist: $d')
- continue
- if (mode & DIRPERMS) <> DIRPERMS:
- STATE.ERRORS += 1
- print _('directory must be at least 02775: $d'),
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(d, mode | DIRPERMS)
- else:
- print
- # check all subdirs
- os.path.walk(d, checkwalk, STATE)
-
-
-
-def checkarchives():
- private = config.PRIVATE_ARCHIVE_FILE_DIR
- if STATE.VERBOSE:
- print _('checking perms on $private')
- # private archives must not be other readable
- mode = statmode(private)
- if mode & S_IROTH:
- STATE.ERRORS += 1
- print _('$private must not be other-readable'),
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(private, mode & ~S_IROTH)
- else:
- print
- # In addition, on a multiuser system you may want to hide the private
- # archives so other users can't read them.
- if mode & S_IXOTH:
- print _("""\
-Warning: Private archive directory is other-executable (o+x).
- This could allow other users on your system to read private archives.
- If you're on a shared multiuser system, you should consult the
- installation manual on how to fix this.""")
-
-
-
-def checkmboxfile(mboxdir):
- absdir = os.path.join(config.PRIVATE_ARCHIVE_FILE_DIR, mboxdir)
- for f in os.listdir(absdir):
- if not f.endswith('.mbox'):
- continue
- mboxfile = os.path.join(absdir, f)
- mode = statmode(mboxfile)
- if (mode & MBOXPERMS) <> MBOXPERMS:
- STATE.ERRORS = STATE.ERRORS + 1
- print _('mbox file must be at least 0660:'), mboxfile
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(mboxfile, mode | MBOXPERMS)
- else:
- print
-
-
-
-def checkarchivedbs():
- # The archives/private/listname/database file must not be other readable
- # or executable otherwise those files will be accessible when the archives
- # are public. That may not be a horrible breach, but let's close this off
- # anyway.
- for dir in os.listdir(config.PRIVATE_ARCHIVE_FILE_DIR):
- if dir.endswith('.mbox'):
- checkmboxfile(dir)
- dbdir = os.path.join(config.PRIVATE_ARCHIVE_FILE_DIR, dir, 'database')
- try:
- mode = statmode(dbdir)
- except OSError, e:
- if e.errno not in (errno.ENOENT, errno.ENOTDIR): raise
- continue
- if mode & S_IRWXO:
- STATE.ERRORS += 1
- print _('$dbdir "other" perms must be 000'),
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(dbdir, mode & ~S_IRWXO)
- else:
- print
-
-
-
-def checkcgi():
- cgidir = os.path.join(config.EXEC_PREFIX, 'cgi-bin')
- if STATE.VERBOSE:
- print _('checking cgi-bin permissions')
- exes = os.listdir(cgidir)
- for f in exes:
- path = os.path.join(cgidir, f)
- if STATE.VERBOSE:
- print _(' checking set-gid for $path')
- mode = statmode(path)
- if mode & S_IXGRP and not mode & S_ISGID:
- STATE.ERRORS += 1
- print _('$path must be set-gid'),
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(path, mode | S_ISGID)
- else:
- print
-
-
-
-def checkmail():
- wrapper = os.path.join(config.WRAPPER_DIR, 'mailman')
- if STATE.VERBOSE:
- print _('checking set-gid for $wrapper')
- mode = statmode(wrapper)
- if not mode & S_ISGID:
- STATE.ERRORS += 1
- print _('$wrapper must be set-gid'),
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(wrapper, mode | S_ISGID)
-
-
-
-def checkadminpw():
- for pwfile in (os.path.join(config.DATA_DIR, 'adm.pw'),
- os.path.join(config.DATA_DIR, 'creator.pw')):
- targetmode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP
- if STATE.VERBOSE:
- print _('checking permissions on $pwfile')
- try:
- mode = statmode(pwfile)
- except OSError, e:
- if e.errno <> errno.ENOENT:
- raise
- return
- if mode <> targetmode:
- STATE.ERRORS += 1
- octmode = oct(mode)
- print _('$pwfile permissions must be exactly 0640 (got $octmode)'),
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(pwfile, targetmode)
- else:
- print
-
-
-def checkmta():
- if config.MTA:
- modname = 'mailman.MTA.' + config.MTA
- __import__(modname)
- try:
- sys.modules[modname].checkperms(STATE)
- except AttributeError:
- pass
-
-
-
-def checkdata():
- targetmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
- checkfiles = ('config.pck', 'config.pck.last',
- 'config.db', 'config.db.last',
- 'next-digest', 'next-digest-topics',
- 'digest.mbox', 'pending.pck',
- 'request.db', 'request.db.tmp')
- if STATE.VERBOSE:
- print _('checking permissions on list data')
- for dir in os.listdir(config.LIST_DATA_DIR):
- for file in checkfiles:
- path = os.path.join(config.LIST_DATA_DIR, dir, file)
- if STATE.VERBOSE:
- print _(' checking permissions on: $path')
- try:
- mode = statmode(path)
- except OSError, e:
- if e.errno <> errno.ENOENT:
- raise
- continue
- if (mode & targetmode) <> targetmode:
- STATE.ERRORS += 1
- print _('file permissions must be at least 660: $path'),
- if STATE.FIX:
- print _('(fixing)')
- os.chmod(path, mode | targetmode)
- else:
- print
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-%prog [options]
-
-Check the permissions of all Mailman files. With no options, just report the
-permission and ownership problems found."""))
- parser.add_option('-f', '--fix',
- default=False, action='store_true', help=_("""\
-Fix all permission and ownership problems found. With this option, you must
-run check_perms as root."""))
- parser.add_option('-v', '--verbose',
- default=False, action='store_true',
- help=_('Produce more verbose output'))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- if args:
- parser.print_help()
- print >> sys.stderr, _('Unexpected arguments')
- sys.exit(1)
- return parser, opts, args
-
-
-
-def main():
- global MAILMAN_USER, MAILMAN_GROUP, MAILMAN_UID, MAILMAN_GID
-
- parser, opts, args = parseargs()
- STATE.FIX = opts.fix
- STATE.VERBOSE = opts.verbose
-
- config.load(opts.config)
-
- MAILMAN_USER = config.MAILMAN_USER
- MAILMAN_GROUP = config.MAILMAN_GROUP
- # Let KeyErrors percolate
- MAILMAN_GID = grp.getgrnam(MAILMAN_GROUP).gr_gid
- MAILMAN_UID = pwd.getpwnam(MAILMAN_USER).pw_uid
-
- checkall()
- checkarchives()
- checkarchivedbs()
- checkcgi()
- checkmail()
- checkdata()
- checkadminpw()
- checkmta()
-
- if not STATE.ERRORS:
- print _('No problems found')
- else:
- print _('Problems found:'), STATE.ERRORS
- print _('Re-run as $MAILMAN_USER (or root) with -f flag to fix')
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/checkdbs.py b/mailman/bin/checkdbs.py
deleted file mode 100644
index 2ce08aab7..000000000
--- a/mailman/bin/checkdbs.py
+++ /dev/null
@@ -1,199 +0,0 @@
-# Copyright (C) 1998-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/>.
-
-import sys
-import time
-import optparse
-
-from email.Charset import Charset
-
-from mailman import MailList
-from mailman import Message
-from mailman import Utils
-from mailman import i18n
-from mailman.app.requests import handle_request
-from mailman.configuration import config
-from mailman.version import MAILMAN_VERSION
-
-_ = i18n._
-
-# Work around known problems with some RedHat cron daemons
-import signal
-signal.signal(signal.SIGCHLD, signal.SIG_DFL)
-
-NL = u'\n'
-now = time.time()
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-%prog [options]
-
-Check for pending admin requests and mail the list owners if necessary."""))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- if args:
- parser.print_help()
- print >> sys.stderr, _('Unexpected arguments')
- sys.exit(1)
- return opts, args, parser
-
-
-
-def pending_requests(mlist):
- # Must return a byte string
- lcset = Utils.GetCharSet(mlist.preferred_language)
- pending = []
- first = True
- requestsdb = config.db.get_list_requests(mlist)
- for request in requestsdb.of_type(RequestType.subscription):
- if first:
- pending.append(_('Pending subscriptions:'))
- first = False
- key, data = requestsdb.get_request(request.id)
- when = data['when']
- addr = data['addr']
- fullname = data['fullname']
- passwd = data['passwd']
- digest = data['digest']
- lang = data['lang']
- if fullname:
- if isinstance(fullname, unicode):
- fullname = fullname.encode(lcset, 'replace')
- fullname = ' (%s)' % fullname
- pending.append(' %s%s %s' % (addr, fullname, time.ctime(when)))
- first = True
- for request in requestsdb.of_type(RequestType.held_message):
- if first:
- pending.append(_('\nPending posts:'))
- first = False
- key, data = requestsdb.get_request(request.id)
- when = data['when']
- sender = data['sender']
- subject = data['subject']
- reason = data['reason']
- text = data['text']
- msgdata = data['msgdata']
- subject = Utils.oneline(subject, lcset)
- date = time.ctime(when)
- reason = _(reason)
- pending.append(_("""\
-From: $sender on $date
-Subject: $subject
-Cause: $reason"""))
- pending.append('')
- # Coerce all items in pending to a Unicode so we can join them
- upending = []
- charset = Utils.GetCharSet(mlist.preferred_language)
- for s in pending:
- if isinstance(s, unicode):
- upending.append(s)
- else:
- upending.append(unicode(s, charset, 'replace'))
- # Make sure that the text we return from here can be encoded to a byte
- # string in the charset of the list's language. This could fail if for
- # example, the request was pended while the list's language was French,
- # but then it was changed to English before checkdbs ran.
- text = NL.join(upending)
- charset = Charset(Utils.GetCharSet(mlist.preferred_language))
- incodec = charset.input_codec or 'ascii'
- outcodec = charset.output_codec or 'ascii'
- if isinstance(text, unicode):
- return text.encode(outcodec, 'replace')
- # Be sure this is a byte string encodeable in the list's charset
- utext = unicode(text, incodec, 'replace')
- return utext.encode(outcodec, 'replace')
-
-
-
-def auto_discard(mlist):
- # Discard old held messages
- discard_count = 0
- expire = config.days(mlist.max_days_to_hold)
- requestsdb = config.db.get_list_requests(mlist)
- heldmsgs = list(requestsdb.of_type(RequestType.held_message))
- if expire and heldmsgs:
- for request in heldmsgs:
- key, data = requestsdb.get_request(request.id)
- if now - data['date'] > expire:
- handle_request(mlist, request.id, config.DISCARD)
- discard_count += 1
- mlist.Save()
- return discard_count
-
-
-
-def main():
- opts, args, parser = parseargs()
- config.load(opts.config)
-
- i18n.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
- for name in config.list_manager.names:
- # The list must be locked in order to open the requests database
- mlist = MailList.MailList(name)
- try:
- count = config.db.requests.get_list_requests(mlist).count
- # While we're at it, let's evict yesterday's autoresponse data
- midnight_today = Utils.midnight()
- evictions = []
- for sender in mlist.hold_and_cmd_autoresponses.keys():
- date, respcount = mlist.hold_and_cmd_autoresponses[sender]
- if Utils.midnight(date) < midnight_today:
- evictions.append(sender)
- if evictions:
- for sender in evictions:
- del mlist.hold_and_cmd_autoresponses[sender]
- # This is the only place we've changed the list's database
- mlist.Save()
- if count:
- i18n.set_language(mlist.preferred_language)
- realname = mlist.real_name
- discarded = auto_discard(mlist)
- if discarded:
- count = count - discarded
- text = _(
- 'Notice: $discarded old request(s) automatically expired.\n\n')
- else:
- text = ''
- if count:
- text += Utils.maketext(
- 'checkdbs.txt',
- {'count' : count,
- 'host_name': mlist.host_name,
- 'adminDB' : mlist.GetScriptURL('admindb', absolute=1),
- 'real_name': realname,
- }, mlist=mlist)
- text += '\n' + pending_requests(mlist)
- subject = _('$count $realname moderator request(s) waiting')
- else:
- subject = _('$realname moderator request check result')
- msg = Message.UserNotification(mlist.GetOwnerEmail(),
- mlist.GetBouncesEmail(),
- subject, text,
- mlist.preferred_language)
- msg.send(mlist, **{'tomoderators': True})
- finally:
- mlist.Unlock()
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/cleanarch.py b/mailman/bin/cleanarch.py
deleted file mode 100644
index 325fad91a..000000000
--- a/mailman/bin/cleanarch.py
+++ /dev/null
@@ -1,133 +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/>.
-
-"""Clean up an .mbox archive file."""
-
-import re
-import sys
-import mailbox
-import optparse
-
-from mailman.i18n import _
-from mailman.version import MAILMAN_VERSION
-
-
-cre = re.compile(mailbox.UnixMailbox._fromlinepattern)
-# From RFC 2822, a header field name must contain only characters from 33-126
-# inclusive, excluding colon. I.e. from oct 41 to oct 176 less oct 072. Must
-# use re.match() so that it's anchored at the beginning of the line.
-fre = re.compile(r'[\041-\071\073-\176]+')
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-%prog [options] < inputfile > outputfile
-
-The archiver looks for Unix-From lines separating messages in an mbox archive
-file. For compatibility, it specifically looks for lines that start with
-'From ' -- i.e. the letters capital-F, lowercase-r, o, m, space, ignoring
-everything else on the line.
-
-Normally, any lines that start 'From ' in the body of a message should be
-escaped such that a > character is actually the first on a line. It is
-possible though that body lines are not actually escaped. This script
-attempts to fix these by doing a stricter test of the Unix-From lines. Any
-lines that start From ' but do not pass this stricter test are escaped with a
-'>' character."""))
- parser.add_option('-q', '--quiet',
- default=False, action='store_true', help=_("""\
-Don't print changed line information to standard error."""))
- parser.add_option('-s', '--status',
- default=-1, type='int', help=_("""\
-Print a '#' character for every n lines processed. With a number less than or
-equal to zero, suppress the '#' characters."""))
- parser.add_option('-n', '--dry-run',
- default=False, action='store_true', help=_("""\
-Don't actually output anything."""))
- opts, args = parser.parser_args()
- if args:
- parser.print_error(_('Unexpected arguments'))
- return parser, opts, args
-
-
-
-def escape_line(line, lineno, quiet, output):
- if output:
- sys.stdout.write('>' + line)
- if not quiet:
- print >> sys.stderr, _('Unix-From line changed: $lineno')
- print >> sys.stderr, line[:-1]
-
-
-
-def main():
- parser, opts, args = parseargs()
-
- lineno = 0
- statuscnt = 0
- messages = 0
- prevline = None
- while True:
- lineno += 1
- line = sys.stdin.readline()
- if not line:
- break
- if line.startswith('From '):
- if cre.match(line):
- # This is a real Unix-From line. But it could be a message
- # /about/ Unix-From lines, so as a second order test, make
- # sure there's at least one RFC 2822 header following
- nextline = sys.stdin.readline()
- lineno += 1
- if not nextline:
- # It was the last line of the mbox, so it couldn't have
- # been a Unix-From
- escape_line(line, lineno, quiet, output)
- break
- fieldname = nextline.split(':', 1)
- if len(fieldname) < 2 or not fre.match(nextline):
- # The following line was not a header, so this wasn't a
- # valid Unix-From
- escape_line(line, lineno, quiet, output)
- if output:
- sys.stdout.write(nextline)
- else:
- # It's a valid Unix-From line
- messages += 1
- if output:
- # Before we spit out the From_ line, make sure the
- # previous line was blank.
- if prevline is not None and prevline <> '\n':
- sys.stdout.write('\n')
- sys.stdout.write(line)
- sys.stdout.write(nextline)
- else:
- # This is a bogus Unix-From line
- escape_line(line, lineno, quiet, output)
- elif output:
- # Any old line
- sys.stdout.write(line)
- if status > 0 and (lineno % status) == 0:
- sys.stderr.write('#')
- statuscnt += 1
- if statuscnt > 50:
- print >> sys.stderr
- statuscnt = 0
- prevline = line
- print >> sys.stderr, _('%(messages)d messages found')
diff --git a/mailman/bin/config_list.py b/mailman/bin/config_list.py
deleted file mode 100644
index a5cec9480..000000000
--- a/mailman/bin/config_list.py
+++ /dev/null
@@ -1,332 +0,0 @@
-# Copyright (C) 1998-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/>.
-
-import re
-import sys
-import time
-import optparse
-
-from mailman import errors
-from mailman import MailList
-from mailman import Utils
-from mailman import i18n
-from mailman.configuration import config
-from mailman.version import MAILMAN_VERSION
-
-_ = i18n._
-
-NL = '\n'
-nonasciipat = re.compile(r'[\x80-\xff]')
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-%prog [options] listname
-
-Configure a list from a text file description, or dump a list's configuration
-settings."""))
- parser.add_option('-i', '--inputfile',
- metavar='FILENAME', default=None, type='string',
- help=_("""\
-Configure the list by assigning each module-global variable in the file to an
-attribute on the mailing list object, then save the list. The named file is
-loaded with execfile() and must be legal Python code. Any variable that isn't
-already an attribute of the list object is ignored (a warning message is
-printed). See also the -c option.
-
-A special variable named 'mlist' is put into the globals during the execfile,
-which is bound to the actual MailList object. This lets you do all manner of
-bizarre thing to the list object, but BEWARE! Using this can severely (and
-possibly irreparably) damage your mailing list!
-
-The may not be used with the -o option."""))
- parser.add_option('-o', '--outputfile',
- metavar='FILENAME', default=None, type='string',
- help=_("""\
-Instead of configuring the list, print out a mailing list's configuration
-variables in a format suitable for input using this script. In this way, you
-can easily capture the configuration settings for a particular list and
-imprint those settings on another list. FILENAME is the file to output the
-settings to. If FILENAME is `-', standard out is used.
-
-This may not be used with the -i option."""))
- parser.add_option('-c', '--checkonly',
- default=False, action='store_true', help=_("""\
-With this option, the modified list is not actually changed. This is only
-useful with the -i option."""))
- parser.add_option('-v', '--verbose',
- default=False, action='store_true', help=_("""\
-Print the name of each attribute as it is being changed. This is only useful
-with the -i option."""))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- if len(args) > 1:
- parser.print_help()
- parser.error(_('Unexpected arguments'))
- if not args:
- parser.error(_('List name is required'))
- return parser, opts, args
-
-
-
-def do_output(listname, outfile, parser):
- closep = False
- try:
- if outfile == '-':
- outfp = sys.stdout
- else:
- outfp = open(outfile, 'w')
- closep = True
- # Open the specified list unlocked, since we're only reading it.
- try:
- mlist = MailList.MailList(listname, lock=False)
- except errors.MMListError:
- parser.error(_('No such list: $listname'))
- # Preamble for the config info. PEP 263 charset and capture time.
- language = mlist.preferred_language
- charset = Utils.GetCharSet(language)
- i18n.set_language(language)
- if not charset:
- charset = 'us-ascii'
- when = time.ctime(time.time())
- print >> outfp, _('''\
-# -*- python -*-
-# -*- coding: $charset -*-
-## "$listname" mailing list configuration settings
-## captured on $when
-''')
- # Get all the list config info. All this stuff is accessible via the
- # web interface.
- for k in config.ADMIN_CATEGORIES:
- subcats = mlist.GetConfigSubCategories(k)
- if subcats is None:
- do_list_categories(mlist, k, None, outfp)
- else:
- for subcat in [t[0] for t in subcats]:
- do_list_categories(mlist, k, subcat, outfp)
- finally:
- if closep:
- outfp.close()
-
-
-
-def do_list_categories(mlist, k, subcat, outfp):
- info = mlist.GetConfigInfo(k, subcat)
- label, gui = mlist.GetConfigCategories()[k]
- if info is None:
- return
- charset = Utils.GetCharSet(mlist.preferred_language)
- print >> outfp, '##', k.capitalize(), _('options')
- print >> outfp, '#'
- # First, massage the descripton text, which could have obnoxious
- # leading whitespace on second and subsequent lines due to
- # triple-quoted string nonsense in the source code.
- desc = NL.join([s.lstrip() for s in info[0].splitlines()])
- # Print out the category description
- desc = Utils.wrap(desc)
- for line in desc.splitlines():
- print >> outfp, '#', line
- print >> outfp
- for data in info[1:]:
- if not isinstance(data, tuple):
- continue
- varname = data[0]
- # Variable could be volatile
- if varname[0] == '_':
- continue
- vtype = data[1]
- # First, massage the descripton text, which could have
- # obnoxious leading whitespace on second and subsequent lines
- # due to triple-quoted string nonsense in the source code.
- desc = NL.join([s.lstrip() for s in data[-1].splitlines()])
- # Now strip out all HTML tags
- desc = re.sub('<.*?>', '', desc)
- # And convert &lt;/&gt; to <>
- desc = re.sub('&lt;', '<', desc)
- desc = re.sub('&gt;', '>', desc)
- # Print out the variable description.
- desc = Utils.wrap(desc)
- for line in desc.split('\n'):
- print >> outfp, '#', line
- # munge the value based on its type
- value = None
- if hasattr(gui, 'getValue'):
- value = gui.getValue(mlist, vtype, varname, data[2])
- if value is None and not varname.startswith('_'):
- value = getattr(mlist, varname)
- if vtype in (config.String, config.Text, config.FileUpload):
- print >> outfp, varname, '=',
- lines = value.splitlines()
- if not lines:
- print >> outfp, "''"
- elif len(lines) == 1:
- if charset <> 'us-ascii' and nonasciipat.search(lines[0]):
- # This is more readable for non-english list.
- print >> outfp, '"' + lines[0].replace('"', '\\"') + '"'
- else:
- print >> outfp, repr(lines[0])
- else:
- if charset == 'us-ascii' and nonasciipat.search(value):
- # Normally, an english list should not have non-ascii char.
- print >> outfp, repr(NL.join(lines))
- else:
- outfp.write(' """')
- outfp.write(NL.join(lines).replace('"', '\\"'))
- outfp.write('"""\n')
- elif vtype in (config.Radio, config.Toggle):
- print >> outfp, '#'
- print >> outfp, '#', _('legal values are:')
- # TBD: This is disgusting, but it's special cased
- # everywhere else anyway...
- if varname == 'subscribe_policy' and \
- not config.ALLOW_OPEN_SUBSCRIBE:
- i = 1
- else:
- i = 0
- for choice in data[2]:
- print >> outfp, '# ', i, '= "%s"' % choice
- i += 1
- print >> outfp, varname, '=', repr(value)
- else:
- print >> outfp, varname, '=', repr(value)
- print >> outfp
-
-
-
-def getPropertyMap(mlist):
- guibyprop = {}
- categories = mlist.GetConfigCategories()
- for category, (label, gui) in categories.items():
- if not hasattr(gui, 'GetConfigInfo'):
- continue
- subcats = mlist.GetConfigSubCategories(category)
- if subcats is None:
- subcats = [(None, None)]
- for subcat, sclabel in subcats:
- for element in gui.GetConfigInfo(mlist, category, subcat):
- if not isinstance(element, tuple):
- continue
- propname = element[0]
- wtype = element[1]
- guibyprop[propname] = (gui, wtype)
- return guibyprop
-
-
-class FakeDoc:
- # Fake the error reporting API for the htmlformat.Document class
- def addError(self, s, tag=None, *args):
- if tag:
- print >> sys.stderr, tag
- print >> sys.stderr, s % args
-
- def set_language(self, val):
- pass
-
-
-
-def do_input(listname, infile, checkonly, verbose, parser):
- fakedoc = FakeDoc()
- # Open the specified list locked, unless checkonly is set
- try:
- mlist = MailList.MailList(listname, lock=not checkonly)
- except errors.MMListError, e:
- parser.error(_('No such list "$listname"\n$e'))
- savelist = False
- guibyprop = getPropertyMap(mlist)
- try:
- globals = {'mlist': mlist}
- # Any exception that occurs in execfile() will cause the list to not
- # be saved, but any other problems are not save-fatal.
- execfile(infile, globals)
- savelist = True
- for k, v in globals.items():
- if k in ('mlist', '__builtins__'):
- continue
- if not hasattr(mlist, k):
- print >> sys.stderr, _('attribute "$k" ignored')
- continue
- if verbose:
- print >> sys.stderr, _('attribute "$k" changed')
- missing = []
- gui, wtype = guibyprop.get(k, (missing, missing))
- if gui is missing:
- # This isn't an official property of the list, but that's
- # okay, we'll just restore it the old fashioned way
- print >> sys.stderr, _('Non-standard property restored: $k')
- setattr(mlist, k, v)
- else:
- # BAW: This uses non-public methods. This logic taken from
- # the guts of GUIBase.handleForm().
- try:
- validval = gui._getValidValue(mlist, k, wtype, v)
- except ValueError:
- print >> sys.stderr, _('Invalid value for property: $k')
- except errors.EmailAddressError:
- print >> sys.stderr, _(
- 'Bad email address for option $k: $v')
- else:
- # BAW: Horrible hack, but then this is special cased
- # everywhere anyway. :( Privacy._setValue() knows that
- # when ALLOW_OPEN_SUBSCRIBE is false, the web values are
- # 0, 1, 2 but these really should be 1, 2, 3, so it adds
- # one. But we really do provide [0..3] so we need to undo
- # the hack that _setValue adds. :( :(
- if k == 'subscribe_policy' and \
- not config.ALLOW_OPEN_SUBSCRIBE:
- validval -= 1
- # BAW: Another horrible hack. This one is just too hard
- # to fix in a principled way in Mailman 2.1
- elif k == 'new_member_options':
- # Because this is a Checkbox, _getValidValue()
- # transforms the value into a list of one item.
- validval = validval[0]
- validval = [bitfield for bitfield, bitval
- in config.OPTINFO.items()
- if validval & bitval]
- gui._setValue(mlist, k, validval, fakedoc)
- # BAW: when to do gui._postValidate()???
- finally:
- if savelist and not checkonly:
- mlist.Save()
- mlist.Unlock()
-
-
-
-def main():
- parser, opts, args = parseargs()
- config.load(opts.config)
- listname = args[0]
-
- # Sanity check
- if opts.inputfile and opts.outputfile:
- parser.error(_('Only one of -i or -o is allowed'))
- if not opts.inputfile and not opts.outputfile:
- parser.error(_('One of -i or -o is required'))
-
- if opts.outputfile:
- do_output(listname, opts.outputfile, parser)
- else:
- do_input(listname, opts.inputfile, opts.checkonly,
- opts.verbose, parser)
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/create_list.py b/mailman/bin/create_list.py
deleted file mode 100644
index 8058a7d67..000000000
--- a/mailman/bin/create_list.py
+++ /dev/null
@@ -1,129 +0,0 @@
-# Copyright (C) 1998-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/>.
-
-import sys
-
-from mailman import Message
-from mailman import Utils
-from mailman import i18n
-from mailman.app.lifecycle import create_list
-from mailman.config import config
-from mailman.core import errors
-from mailman.interfaces.listmanager import ListAlreadyExistsError
-from mailman.options import SingleMailingListOptions
-
-
-_ = i18n._
-
-
-
-class ScriptOptions(SingleMailingListOptions):
- usage = _("""\
-%prog [options]
-
-Create a new mailing list.
-
-fqdn_listname is the 'fully qualified list name', basically the posting
-address of the list. It must be a valid email address and the domain must be
-registered with Mailman.
-
-Note that listnames are forced to lowercase.""")
-
- def add_options(self):
- super(ScriptOptions, self).add_options()
- self.parser.add_option(
- '--language',
- type='unicode', action='store',
- help=_("""\
-Make the list's preferred language LANGUAGE, which must be a two letter
-language code."""))
- self.parser.add_option(
- '-o', '--owner',
- type='unicode', action='append', default=[],
- dest='owners', help=_("""\
-Specific a listowner email address. If the address is not currently
-registered with Mailman, the address is registered and linked to a user.
-Mailman will send a confirmation message to the address, but it will also send
-a list creation notice to the address. More than one owner can be
-specified."""))
- self.parser.add_option(
- '-q', '--quiet',
- default=False, action='store_true',
- help=_("""\
-Normally the administrator is notified by email (after a prompt) that their
-list has been created. This option suppresses the prompt and
-notification."""))
- self.parser.add_option(
- '-a', '--automate',
- default=False, action='store_true',
- help=_("""\
-This option suppresses the prompt prior to administrator notification but
-still sends the notification. It can be used to make newlist totally
-non-interactive but still send the notification, assuming at least one list
-owner is specified with the -o option.."""))
-
- def sanity_check(self):
- """Set up some defaults we couldn't set up earlier."""
- if self.options.language is None:
- self.options.language = unicode(config.mailman.default_language)
- # Is the language known?
- if self.options.language not in config.languages.enabled_codes:
- self.parser.error(_('Unknown language: $opts.language'))
- # Handle variable number of positional arguments
- if len(self.arguments) > 0:
- parser.error(_('Unexpected arguments'))
-
-
-
-def main():
- options = ScriptOptions()
- options.initialize()
-
- # Create the mailing list, applying styles as appropriate.
- fqdn_listname = options.options.listname
- if fqdn_listname is None:
- options.parser.error(_('--listname is required'))
- try:
- mlist = create_list(fqdn_listname, options.options.owners)
- mlist.preferred_language = options.options.language
- except errors.InvalidEmailAddress:
- options.parser.error(_('Illegal list name: $fqdn_listname'))
- except ListAlreadyExistsError:
- options.parser.error(_('List already exists: $fqdn_listname'))
- except errors.BadDomainSpecificationError, domain:
- options.parser.error(_('Undefined domain: $domain'))
-
- config.db.commit()
-
- if not options.options.quiet:
- d = dict(
- listname = mlist.fqdn_listname,
- admin_url = mlist.script_url('admin'),
- listinfo_url = mlist.script_url('listinfo'),
- requestaddr = mlist.request_address,
- siteowner = mlist.no_reply_address,
- )
- text = Utils.maketext('newlist.txt', d, mlist=mlist)
- # Set the I18N language to the list's preferred language so the header
- # will match the template language. Stashing and restoring the old
- # translation context is just (healthy? :) paranoia.
- with i18n.using_language(mlist.preferred_language):
- msg = Message.UserNotification(
- owner_mail, mlist.no_reply_address,
- _('Your new mailing list: $fqdn_listname'),
- text, mlist.preferred_language)
- msg.send(mlist)
diff --git a/mailman/bin/disabled.py b/mailman/bin/disabled.py
deleted file mode 100644
index cc8eb2c69..000000000
--- a/mailman/bin/disabled.py
+++ /dev/null
@@ -1,201 +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/>.
-
-import time
-import logging
-import optparse
-
-from mailman import errors
-from mailman import MailList
-from mailman import MemberAdaptor
-from mailman import Pending
-from mailman import loginit
-from mailman.Bouncer import _BounceInfo
-from mailman.configuration import config
-from mailman.i18n import _
-from mailman.version import MAILMAN_VERSION
-
-
-# Work around known problems with some RedHat cron daemons
-import signal
-signal.signal(signal.SIGCHLD, signal.SIG_DFL)
-
-ALL = (MemberAdaptor.BYBOUNCE,
- MemberAdaptor.BYADMIN,
- MemberAdaptor.BYUSER,
- MemberAdaptor.UNKNOWN,
- )
-
-
-
-def who_callback(option, opt, value, parser):
- dest = getattr(parser.values, option.dest)
- if opt in ('-o', '--byadmin'):
- dest.add(MemberAdaptor.BYADMIN)
- elif opt in ('-m', '--byuser'):
- dest.add(MemberAdaptor.BYUSER)
- elif opt in ('-u', '--unknown'):
- dest.add(MemberAdaptor.UNKNOWN)
- elif opt in ('-b', '--notbybounce'):
- dest.discard(MemberAdaptor.BYBOUNCE)
- elif opt in ('-a', '--all'):
- dest.update(ALL)
-
-
-def parseargs():
- parser = optparse.OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-%prog [options]
-
-Process disabled members, recommended once per day.
-
-This script iterates through every mailing list looking for members whose
-delivery is disabled. If they have been disabled due to bounces, they will
-receive another notification, or they may be removed if they've received the
-maximum number of notifications.
-
-Use the --byadmin, --byuser, and --unknown flags to also send notifications to
-members whose accounts have been disabled for those reasons. Use --all to
-send the notification to all disabled members."""))
- # This is the set of working flags for who to send notifications to. By
- # default, we notify anybody who has been disable due to bounces.
- parser.set_defaults(who=set([MemberAdaptor.BYBOUNCE]))
- parser.add_option('-o', '--byadmin',
- callback=who_callback, action='callback', dest='who',
- help=_("""\
-Also send notifications to any member disabled by the list
-owner/administrator."""))
- parser.add_option('-m', '--byuser',
- callback=who_callback, action='callback', dest='who',
- help=_("""\
-Also send notifications to any member who has disabled themself."""))
- parser.add_option('-u', '--unknown',
- callback=who_callback, action='callback', dest='who',
- help=_("""\
-Also send notifications to any member disabled for unknown reasons
-(usually a legacy disabled address)."""))
- parser.add_option('-b', '--notbybounce',
- callback=who_callback, action='callback', dest='who',
- help=_("""\
-Don't send notifications to members disabled because of bounces (the
-default is to notify bounce disabled members)."""))
- parser.add_option('-a', '--all',
- callback=who_callback, action='callback', dest='who',
- help=_('Send notifications to all disabled members'))
- parser.add_option('-f', '--force',
- default=False, action='store_true',
- help=_("""\
-Send notifications to disabled members even if they're not due a new
-notification yet."""))
- parser.add_option('-l', '--listname',
- dest='listnames', action='append', default=[],
- type='string', help=_("""\
-Process only the given list, otherwise do all lists."""))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- return opts, args, parser
-
-
-
-def main():
- opts, args, parser = parseargs()
- config.load(opts.config)
-
- loginit.initialize(propagate=True)
- elog = logging.getLogger('mailman.error')
- blog = logging.getLogger('mailman.bounce')
-
- listnames = set(opts.listnames or config.list_manager.names)
- who = tuple(opts.who)
-
- msg = _('[disabled by periodic sweep and cull, no message available]')
- today = time.mktime(time.localtime()[:3] + (0,) * 6)
- for listname in listnames:
- # List of members to notify
- notify = []
- mlist = MailList.MailList(listname)
- try:
- interval = mlist.bounce_you_are_disabled_warnings_interval
- # Find all the members who are currently bouncing and see if
- # they've reached the disable threshold but haven't yet been
- # disabled. This is a sweep through the membership catching
- # situations where they've bounced a bunch, then the list admin
- # lowered the threshold, but we haven't (yet) seen more bounces
- # from the member. Note: we won't worry about stale information
- # or anything else since the normal bounce processing code will
- # handle that.
- disables = []
- for member in mlist.getBouncingMembers():
- if mlist.getDeliveryStatus(member) <> MemberAdaptor.ENABLED:
- continue
- info = mlist.getBounceInfo(member)
- if info.score >= mlist.bounce_score_threshold:
- disables.append((member, info))
- if disables:
- for member, info in disables:
- mlist.disableBouncingMember(member, info, msg)
- # Go through all the members who have delivery disabled, and find
- # those that are due to have another notification. If they are
- # disabled for another reason than bouncing, and we're processing
- # them (because of the command line switch) then they won't have a
- # bounce info record. We can piggyback on that for all disable
- # purposes.
- members = mlist.getDeliveryStatusMembers(who)
- for member in members:
- info = mlist.getBounceInfo(member)
- if not info:
- # See if they are bounce disabled, or disabled for some
- # other reason.
- status = mlist.getDeliveryStatus(member)
- if status == MemberAdaptor.BYBOUNCE:
- elog.error(
- '%s disabled BYBOUNCE lacks bounce info, list: %s',
- member, mlist.internal_name())
- continue
- info = _BounceInfo(
- member, 0, today,
- mlist.bounce_you_are_disabled_warnings,
- mlist.pend_new(Pending.RE_ENABLE,
- mlist.internal_name(),
- member))
- mlist.setBounceInfo(member, info)
- lastnotice = time.mktime(info.lastnotice + (0,) * 6)
- if opts.force or today >= lastnotice + interval:
- notify.append(member)
- # Now, send notifications to anyone who is due
- for member in notify:
- blog.info('Notifying disabled member %s for list: %s',
- member, mlist.internal_name())
- try:
- mlist.sendNextNotification(member)
- except errors.NotAMemberError:
- # There must have been some problem with the data we have
- # on this member. Most likely it's that they don't have a
- # password assigned. Log this and delete the member.
- blog.info(
- 'NotAMemberError when sending disabled notice: %s',
- member)
- mlist.ApprovedDeleteMember(member, 'cron/disabled')
- mlist.Save()
- finally:
- mlist.Unlock()
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/docs/master.txt b/mailman/bin/docs/master.txt
deleted file mode 100644
index 0d3cade77..000000000
--- a/mailman/bin/docs/master.txt
+++ /dev/null
@@ -1,49 +0,0 @@
-Mailman queue runner control
-============================
-
-Mailman has a number of queue runners which process messages in its queue file
-directories. In normal operation, a command line script called 'mailmanctl'
-is used to start, stop and manage the queue runners. mailmanctl actually is
-just a wrapper around the real queue runner watcher script called master.py.
-
- >>> from mailman.testing.helpers import TestableMaster
-
-Start the master in a subthread.
-
- >>> master = TestableMaster()
- >>> master.start()
-
-There should be a process id for every qrunner that claims to be startable.
-
- >>> from lazr.config import as_boolean
- >>> startable_qrunners = [qconf for qconf in config.qrunner_configs
- ... if as_boolean(qconf.start)]
- >>> len(list(master.qrunner_pids)) == len(startable_qrunners)
- True
-
-Now verify that all the qrunners are running.
-
- >>> import os
-
- # This should produce no output.
- >>> for pid in master.qrunner_pids:
- ... os.kill(pid, 0)
-
-Stop the master process, which should also kill (and not restart) the child
-queue runner processes.
-
- >>> master.stop()
-
-None of the children are running now.
-
- >>> import errno
- >>> for pid in master.qrunner_pids:
- ... try:
- ... os.kill(pid, 0)
- ... print 'Process did not exit:', pid
- ... except OSError, error:
- ... if error.errno == errno.ESRCH:
- ... # The child process exited.
- ... pass
- ... else:
- ... raise
diff --git a/mailman/bin/dumpdb.py b/mailman/bin/dumpdb.py
deleted file mode 100644
index 6657602e4..000000000
--- a/mailman/bin/dumpdb.py
+++ /dev/null
@@ -1,88 +0,0 @@
-# Copyright (C) 1998-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/>.
-
-import pprint
-import cPickle
-
-from mailman.config import config
-from mailman.i18n import _
-from mailman.interact import interact
-from mailman.options import Options
-
-
-COMMASPACE = ', '
-m = []
-
-
-
-class ScriptOptions(Options):
- usage=_("""\
-%prog [options] filename
-
-Dump the contents of any Mailman queue file. The queue file is a data file
-with multiple Python pickles in it.""")
-
- def add_options(self):
- super(ScriptOptions, self).add_options()
- self.parser.add_option(
- '-n', '--noprint',
- dest='doprint', default=True, action='store_false',
- help=_("""\
-Don't attempt to pretty print the object. This is useful if there is some
-problem with the object and you just want to get an unpickled representation.
-Useful with 'bin/dumpdb -i <file>'. In that case, the list of
-unpickled objects will be left in a variable called 'm'."""))
- self.parser.add_option(
- '-i', '--interact',
- default=False, action='store_true',
- help=_("""\
-Start an interactive Python session, with a variable called 'm' containing the
-list of unpickled objects."""))
-
- def sanity_check(self):
- if len(self.arguments) < 1:
- self.parser.error(_('No filename given.'))
- elif len(self.arguments) > 1:
- self.parser.error(_('Unexpected arguments'))
- else:
- self.filename = self.arguments[0]
-
-
-
-def main():
- options = ScriptOptions()
- options.initialize()
-
- pp = pprint.PrettyPrinter(indent=4)
- with open(options.filename) as fp:
- while True:
- try:
- m.append(cPickle.load(fp))
- except EOFError:
- break
- if options.options.doprint:
- print _('[----- start pickle -----]')
- for i, obj in enumerate(m):
- count = i + 1
- print _('<----- start object $count ----->')
- if isinstance(obj, basestring):
- print obj
- else:
- pp.pprint(obj)
- print _('[----- end pickle -----]')
- if options.options.interact:
- interact()
diff --git a/mailman/bin/export.py b/mailman/bin/export.py
deleted file mode 100644
index d1992b4b4..000000000
--- a/mailman/bin/export.py
+++ /dev/null
@@ -1,310 +0,0 @@
-# Copyright (C) 2006-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/>.
-
-"""Export an XML representation of a mailing list."""
-
-import sys
-import codecs
-import datetime
-import optparse
-
-from xml.sax.saxutils import escape
-
-from mailman import Defaults
-from mailman import errors
-from mailman import MemberAdaptor
-from mailman.MailList import MailList
-from mailman.configuration import config
-from mailman.i18n import _
-from mailman.initialize import initialize
-from mailman.version import MAILMAN_VERSION
-
-
-SPACE = ' '
-
-TYPES = {
- Defaults.Toggle : 'bool',
- Defaults.Radio : 'radio',
- Defaults.String : 'string',
- Defaults.Text : 'text',
- Defaults.Email : 'email',
- Defaults.EmailList : 'email_list',
- Defaults.Host : 'host',
- Defaults.Number : 'number',
- Defaults.FileUpload : 'upload',
- Defaults.Select : 'select',
- Defaults.Topics : 'topics',
- Defaults.Checkbox : 'checkbox',
- Defaults.EmailListEx : 'email_list_ex',
- Defaults.HeaderFilter : 'header_filter',
- }
-
-
-
-class Indenter:
- def __init__(self, fp, indentwidth=4):
- self._fp = fp
- self._indent = 0
- self._width = indentwidth
-
- def indent(self):
- self._indent += 1
-
- def dedent(self):
- self._indent -= 1
- assert self._indent >= 0
-
- def write(self, s):
- if s <> '\n':
- self._fp.write(self._indent * self._width * ' ')
- self._fp.write(s)
-
-
-
-class XMLDumper(object):
- def __init__(self, fp):
- self._fp = Indenter(fp)
- self._tagbuffer = None
- self._stack = []
-
- def _makeattrs(self, tagattrs):
- # The attribute values might contain angle brackets. They might also
- # be None.
- attrs = []
- for k, v in tagattrs.items():
- if v is None:
- v = ''
- else:
- v = escape(str(v))
- attrs.append('%s="%s"' % (k, v))
- return SPACE.join(attrs)
-
- def _flush(self, more=True):
- if not self._tagbuffer:
- return
- name, attributes = self._tagbuffer
- self._tagbuffer = None
- if attributes:
- attrstr = ' ' + self._makeattrs(attributes)
- else:
- attrstr = ''
- if more:
- print >> self._fp, '<%s%s>' % (name, attrstr)
- self._fp.indent()
- self._stack.append(name)
- else:
- print >> self._fp, '<%s%s/>' % (name, attrstr)
-
- # Use this method when you know you have sub-elements.
- def _push_element(self, _name, **_tagattrs):
- self._flush()
- self._tagbuffer = (_name, _tagattrs)
-
- def _pop_element(self, _name):
- buffered = bool(self._tagbuffer)
- self._flush(more=False)
- if not buffered:
- name = self._stack.pop()
- assert name == _name, 'got: %s, expected: %s' % (_name, name)
- self._fp.dedent()
- print >> self._fp, '</%s>' % name
-
- # Use this method when you do not have sub-elements
- def _element(self, _name, _value=None, **_attributes):
- self._flush()
- if _attributes:
- attrs = ' ' + self._makeattrs(_attributes)
- else:
- attrs = ''
- if _value is None:
- print >> self._fp, '<%s%s/>' % (_name, attrs)
- else:
- # The value might contain angle brackets.
- value = escape(unicode(_value))
- print >> self._fp, '<%s%s>%s</%s>' % (_name, attrs, value, _name)
-
- def _do_list_categories(self, mlist, k, subcat=None):
- info = mlist.GetConfigInfo(k, subcat)
- label, gui = mlist.GetConfigCategories()[k]
- if info is None:
- return
- for data in info[1:]:
- if not isinstance(data, tuple):
- continue
- varname = data[0]
- # Variable could be volatile
- if varname.startswith('_'):
- continue
- vtype = data[1]
- # Munge the value based on its type
- value = None
- if hasattr(gui, 'getValue'):
- value = gui.getValue(mlist, vtype, varname, data[2])
- if value is None:
- value = getattr(mlist, varname)
- widget_type = TYPES[vtype]
- if isinstance(value, list):
- self._push_element('option', name=varname, type=widget_type)
- for v in value:
- self._element('value', v)
- self._pop_element('option')
- else:
- self._element('option', value, name=varname, type=widget_type)
-
- def _dump_list(self, mlist):
- # Write list configuration values
- self._push_element('list', name=mlist.fqdn_listname)
- self._push_element('configuration')
- self._element('option',
- mlist.preferred_language,
- name='preferred_language')
- for k in config.ADMIN_CATEGORIES:
- subcats = mlist.GetConfigSubCategories(k)
- if subcats is None:
- self._do_list_categories(mlist, k)
- else:
- for subcat in [t[0] for t in subcats]:
- self._do_list_categories(mlist, k, subcat)
- self._pop_element('configuration')
- # Write membership
- self._push_element('roster')
- digesters = set(mlist.getDigestMemberKeys())
- for member in sorted(mlist.getMembers()):
- attrs = dict(id=member)
- cased = mlist.getMemberCPAddress(member)
- if cased <> member:
- attrs['original'] = cased
- self._push_element('member', **attrs)
- self._element('realname', mlist.getMemberName(member))
- self._element('password', mlist.getMemberPassword(member))
- self._element('language', mlist.getMemberLanguage(member))
- # Delivery status, combined with the type of delivery
- attrs = {}
- status = mlist.getDeliveryStatus(member)
- if status == MemberAdaptor.ENABLED:
- attrs['status'] = 'enabled'
- else:
- attrs['status'] = 'disabled'
- attrs['reason'] = {MemberAdaptor.BYUSER : 'byuser',
- MemberAdaptor.BYADMIN : 'byadmin',
- MemberAdaptor.BYBOUNCE : 'bybounce',
- }.get(mlist.getDeliveryStatus(member),
- 'unknown')
- if member in digesters:
- if mlist.getMemberOption(member, Defaults.DisableMime):
- attrs['delivery'] = 'plain'
- else:
- attrs['delivery'] = 'mime'
- else:
- attrs['delivery'] = 'regular'
- changed = mlist.getDeliveryStatusChangeTime(member)
- if changed:
- when = datetime.datetime.fromtimestamp(changed)
- attrs['changed'] = when.isoformat()
- self._element('delivery', **attrs)
- for option, flag in Defaults.OPTINFO.items():
- # Digest/Regular delivery flag must be handled separately
- if option in ('digest', 'plain'):
- continue
- value = mlist.getMemberOption(member, flag)
- self._element(option, value)
- topics = mlist.getMemberTopics(member)
- if not topics:
- self._element('topics')
- else:
- self._push_element('topics')
- for topic in topics:
- self._element('topic', topic)
- self._pop_element('topics')
- self._pop_element('member')
- self._pop_element('roster')
- self._pop_element('list')
-
- def dump(self, listnames):
- print >> self._fp, '<?xml version="1.0" encoding="UTF-8"?>'
- self._push_element('mailman', **{
- 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
- 'xsi:noNamespaceSchemaLocation': 'ssi-1.0.xsd',
- })
- for listname in sorted(listnames):
- try:
- mlist = MailList(listname, lock=False)
- except errors.MMUnknownListError:
- print >> sys.stderr, _('No such list: $listname')
- continue
- self._dump_list(mlist)
- self._pop_element('mailman')
-
- def close(self):
- while self._stack:
- self._pop_element()
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-%prog [options]
-
-Export the configuration and members of a mailing list in XML format."""))
- parser.add_option('-o', '--outputfile',
- metavar='FILENAME', default=None, type='string',
- help=_("""\
-Output XML to FILENAME. If not given, or if FILENAME is '-', standard out is
-used."""))
- parser.add_option('-l', '--listname',
- default=[], action='append', type='string',
- metavar='LISTNAME', dest='listnames', help=_("""\
-The list to include in the output. If not given, then all mailing lists are
-included in the XML output. Multiple -l flags may be given."""))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- if args:
- parser.print_help()
- parser.error(_('Unexpected arguments'))
- return parser, opts, args
-
-
-
-def main():
- parser, opts, args = parseargs()
- initialize(opts.config)
-
- close = False
- if opts.outputfile in (None, '-'):
- writer = codecs.getwriter('utf-8')
- fp = writer(sys.stdout)
- else:
- fp = codecs.open(opts.outputfile, 'w', 'utf-8')
- close = True
-
- try:
- dumper = XMLDumper(fp)
- if opts.listnames:
- listnames = []
- for listname in opts.listnames:
- if '@' not in listname:
- listname = '%s@%s' % (listname, config.DEFAULT_EMAIL_HOST)
- listnames.append(listname)
- else:
- listnames = config.list_manager.names
- dumper.dump(listnames)
- dumper.close()
- finally:
- if close:
- fp.close()
diff --git a/mailman/bin/find_member.py b/mailman/bin/find_member.py
deleted file mode 100644
index 0982724a0..000000000
--- a/mailman/bin/find_member.py
+++ /dev/null
@@ -1,135 +0,0 @@
-# Copyright (C) 1998-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/>.
-
-import re
-import sys
-import optparse
-
-from mailman import errors
-from mailman import MailList
-from mailman.configuration import config
-from mailman.i18n import _
-from mailman.version import MAILMAN_VERSION
-
-
-AS_MEMBER = 0x01
-AS_OWNER = 0x02
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-%prog [options] regex [regex ...]
-
-Find all lists that a member's address is on.
-
-The interaction between -l and -x (see below) is as follows. If any -l option
-is given then only the named list will be included in the search. If any -x
-option is given but no -l option is given, then all lists will be search
-except those specifically excluded.
-
-Regular expression syntax uses the Python 're' module. Complete
-specifications are at:
-
-http://www.python.org/doc/current/lib/module-re.html
-
-Address matches are case-insensitive, but case-preserved addresses are
-displayed."""))
- parser.add_option('-l', '--listname',
- type='string', default=[], action='append',
- dest='listnames',
- help=_('Include only the named list in the search'))
- parser.add_option('-x', '--exclude',
- type='string', default=[], action='append',
- dest='excludes',
- help=_('Exclude the named list from the search'))
- parser.add_option('-w', '--owners',
- default=False, action='store_true',
- help=_('Search list owners as well as members'))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- if not args:
- parser.print_help()
- print >> sys.stderr, _('Search regular expression required')
- sys.exit(1)
- return parser, opts, args
-
-
-
-def main():
- parser, opts, args = parseargs()
- config.load(opts.config)
-
- listnames = opts.listnames or config.list_manager.names
- includes = set(listname.lower() for listname in listnames)
- excludes = set(listname.lower() for listname in opts.excludes)
- listnames = includes - excludes
-
- if not listnames:
- print _('No lists to search')
- return
-
- cres = []
- for r in args:
- cres.append(re.compile(r, re.IGNORECASE))
- # dictionary of {address, (listname, ownerp)}
- matches = {}
- for listname in listnames:
- try:
- mlist = MailList.MailList(listname, lock=False)
- except errors.MMListError:
- print _('No such list: $listname')
- continue
- if opts.owners:
- owners = mlist.owner
- else:
- owners = []
- for cre in cres:
- for member in mlist.getMembers():
- if cre.search(member):
- addr = mlist.getMemberCPAddress(member)
- entries = matches.get(addr, {})
- aswhat = entries.get(listname, 0)
- aswhat |= AS_MEMBER
- entries[listname] = aswhat
- matches[addr] = entries
- for owner in owners:
- if cre.search(owner):
- entries = matches.get(owner, {})
- aswhat = entries.get(listname, 0)
- aswhat |= AS_OWNER
- entries[listname] = aswhat
- matches[owner] = entries
- addrs = matches.keys()
- addrs.sort()
- for k in addrs:
- hits = matches[k]
- lists = hits.keys()
- print k, _('found in:')
- for name in lists:
- aswhat = hits[name]
- if aswhat & AS_MEMBER:
- print ' ', name
- if aswhat & AS_OWNER:
- print ' ', name, _('(as owner)')
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/gate_news.py b/mailman/bin/gate_news.py
deleted file mode 100644
index eac30422d..000000000
--- a/mailman/bin/gate_news.py
+++ /dev/null
@@ -1,243 +0,0 @@
-# Copyright (C) 1998-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/>.
-
-import os
-import sys
-import time
-import socket
-import logging
-import nntplib
-import optparse
-import email.Errors
-
-from email.Parser import Parser
-from locknix import lockfile
-
-from mailman import MailList
-from mailman import Message
-from mailman import Utils
-from mailman import loginit
-from mailman.configuration import config
-from mailman.i18n import _
-from mailman.queue import Switchboard
-from mailman.version import MAILMAN_VERSION
-
-# Work around known problems with some RedHat cron daemons
-import signal
-signal.signal(signal.SIGCHLD, signal.SIG_DFL)
-
-NL = '\n'
-
-log = None
-
-class _ContinueLoop(Exception):
- pass
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-%prog [options]
-
-Poll the NNTP servers for messages to be gatewayed to mailing lists."""))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- if args:
- parser.print_help()
- print >> sys.stderr, _('Unexpected arguments')
- sys.exit(1)
- return opts, args, parser
-
-
-
-_hostcache = {}
-
-def open_newsgroup(mlist):
- # Split host:port if given
- nntp_host, nntp_port = Utils.nntpsplit(mlist.nntp_host)
- # Open up a "mode reader" connection to nntp server. This will be shared
- # for all the gated lists having the same nntp_host.
- conn = _hostcache.get(mlist.nntp_host)
- if conn is None:
- try:
- conn = nntplib.NNTP(nntp_host, nntp_port,
- readermode=True,
- user=config.NNTP_USERNAME,
- password=config.NNTP_PASSWORD)
- except (socket.error, nntplib.NNTPError, IOError), e:
- log.error('error opening connection to nntp_host: %s\n%s',
- mlist.nntp_host, e)
- raise
- _hostcache[mlist.nntp_host] = conn
- # Get the GROUP information for the list, but we're only really interested
- # in the first article number and the last article number
- r, c, f, l, n = conn.group(mlist.linked_newsgroup)
- return conn, int(f), int(l)
-
-
-def clearcache():
- for conn in set(_hostcache.values()):
- conn.quit()
- _hostcache.clear()
-
-
-
-# This function requires the list to be locked.
-def poll_newsgroup(mlist, conn, first, last, glock):
- listname = mlist.internal_name()
- # NEWNEWS is not portable and has synchronization issues.
- for num in range(first, last):
- glock.refresh()
- try:
- headers = conn.head(repr(num))[3]
- found_to = False
- beenthere = False
- for header in headers:
- i = header.find(':')
- value = header[:i].lower()
- if i > 0 and value == 'to':
- found_to = True
- if value <> 'x-beenthere':
- continue
- if header[i:] == ': %s' % mlist.posting_address:
- beenthere = True
- break
- if not beenthere:
- body = conn.body(repr(num))[3]
- # Usenet originated messages will not have a Unix envelope
- # (i.e. "From " header). This breaks Pipermail archiving, so
- # we will synthesize one. Be sure to use the format searched
- # for by mailbox.UnixMailbox._isrealfromline(). BAW: We use
- # the -bounces address here in case any downstream clients use
- # the envelope sender for bounces; I'm not sure about this,
- # but it's the closest to the old semantics.
- lines = ['From %s %s' % (mlist.GetBouncesEmail(),
- time.ctime(time.time()))]
- lines.extend(headers)
- lines.append('')
- lines.extend(body)
- lines.append('')
- p = Parser(Message.Message)
- try:
- msg = p.parsestr(NL.join(lines))
- except email.Errors.MessageError, e:
- log.error('email package exception for %s:%d\n%s',
- mlist.linked_newsgroup, num, e)
- raise _ContinueLoop
- if found_to:
- del msg['X-Originally-To']
- msg['X-Originally-To'] = msg['To']
- del msg['To']
- msg['To'] = mlist.posting_address
- # Post the message to the locked list
- inq = Switchboard(config.INQUEUE_DIR)
- inq.enqueue(msg,
- listname=mlist.internal_name(),
- fromusenet=True)
- log.info('posted to list %s: %7d', listname, num)
- except nntplib.NNTPError, e:
- log.exception('NNTP error for list %s: %7d', listname, num)
- except _ContinueLoop:
- continue
- # Even if we don't post the message because it was seen on the
- # list already, update the watermark
- mlist.usenet_watermark = num
-
-
-
-def process_lists(glock):
- for listname in config.list_manager.names:
- glock.refresh()
- # Open the list unlocked just to check to see if it is gating news to
- # mail. If not, we're done with the list. Otherwise, lock the list
- # and gate the group.
- mlist = MailList.MailList(listname, lock=False)
- if not mlist.gateway_to_mail:
- continue
- # Get the list's watermark, i.e. the last article number that we gated
- # from news to mail. None means that this list has never polled its
- # newsgroup and that we should do a catch up.
- watermark = getattr(mlist, 'usenet_watermark', None)
- # Open the newsgroup, but let most exceptions percolate up.
- try:
- conn, first, last = open_newsgroup(mlist)
- except (socket.error, nntplib.NNTPError):
- break
- log.info('%s: [%d..%d]', listname, first, last)
- try:
- try:
- if watermark is None:
- mlist.Lock(timeout=config.LIST_LOCK_TIMEOUT)
- # This is the first time we've tried to gate this
- # newsgroup. We essentially do a mass catch-up, otherwise
- # we'd flood the mailing list.
- mlist.usenet_watermark = last
- log.info('%s caught up to article %d', listname, last)
- else:
- # The list has been polled previously, so now we simply
- # grab all the messages on the newsgroup that have not
- # been seen by the mailing list. The first such article
- # is the maximum of the lowest article available in the
- # newsgroup and the watermark. It's possible that some
- # articles have been expired since the last time gate_news
- # has run. Not much we can do about that.
- start = max(watermark + 1, first)
- if start > last:
- log.info('nothing new for list %s', listname)
- else:
- mlist.Lock(timeout=config.LIST_LOCK_TIMEOUT)
- log.info('gating %s articles [%d..%d]',
- listname, start, last)
- # Use last+1 because poll_newsgroup() employes a for
- # loop over range, and this will not include the last
- # element in the list.
- poll_newsgroup(mlist, conn, start, last + 1, glock)
- except lockfile.TimeOutError:
- log.error('Could not acquire list lock: %s', listname)
- finally:
- if mlist.Locked():
- mlist.Save()
- mlist.Unlock()
- log.info('%s watermark: %d', listname, mlist.usenet_watermark)
-
-
-
-def main():
- opts, args, parser = parseargs()
- config.load(opts.config)
-
- GATENEWS_LOCK_FILE = os.path.join(config.LOCK_DIR, 'gate_news.lock')
- LOCK_LIFETIME = config.hours(2)
-
- loginit.initialize(propagate=True)
- log = logging.getLogger('mailman.fromusenet')
-
- try:
- with lockfile.Lock(GATENEWS_LOCK_FILE,
- # It's okay to hijack this
- lifetime=LOCK_LIFETIME) as lock:
- process_lists(lock)
- clearcache()
- except lockfile.TimeOutError:
- log.error('Could not acquire gate_news lock')
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/genaliases.py b/mailman/bin/genaliases.py
deleted file mode 100644
index e8916d030..000000000
--- a/mailman/bin/genaliases.py
+++ /dev/null
@@ -1,64 +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/>.
-
-__metaclass__ = type
-__all__ = [
- 'main',
- ]
-
-
-import sys
-
-from mailman.config import config
-from mailman.i18n import _
-from mailman.options import Options
-
-
-
-class ScriptOptions(Options):
- """Options for the genaliases script."""
-
- usage = _("""\
-%prog [options]
-
-Regenerate the Mailman specific MTA aliases from scratch. The actual output
-depends on the value of the 'MTA' variable in your etc/mailman.cfg file.""")
-
- def add_options(self):
- super(ScriptOptions, self).add_options()
- self.parser.add_option(
- '-q', '--quiet',
- default=False, action='store_true', help=_("""\
-Some MTA output can include more verbose help text. Use this to tone down the
-verbosity."""))
-
-
-
-
-def main():
- options = ScriptOptions()
- options.initialize()
-
- # Get the MTA-specific module.
- module_path, class_path = config.mta.incoming.rsplit('.', 1)
- __import__(module_path)
- getattr(sys.modules[module_path], class_path)().regenerate()
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/import.py b/mailman/bin/import.py
deleted file mode 100644
index d2361e808..000000000
--- a/mailman/bin/import.py
+++ /dev/null
@@ -1,315 +0,0 @@
-# Copyright (C) 2006-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/>.
-
-"""Import the XML representation of a mailing list."""
-
-import sys
-import codecs
-import optparse
-import traceback
-
-from xml.dom import minidom
-from xml.parsers.expat import ExpatError
-
-from mailman import Defaults
-from mailman import errors
-from mailman import MemberAdaptor
-from mailman import Utils
-from mailman import passwords
-from mailman.MailList import MailList
-from mailman.i18n import _
-from mailman.initialize import initialize
-from mailman.version import MAILMAN_VERSION
-
-
-OPTS = None
-
-
-
-def nodetext(node):
- # Expect only one TEXT_NODE in the list of children
- for child in node.childNodes:
- if child.nodeType == node.TEXT_NODE:
- return child.data
- return u''
-
-
-def nodegen(node, *elements):
- for child in node.childNodes:
- if child.nodeType <> minidom.Node.ELEMENT_NODE:
- continue
- if elements and child.tagName not in elements:
- print _('Ignoring unexpected element: $node.tagName')
- else:
- yield child
-
-
-
-def parse_config(node):
- config = dict()
- for child in nodegen(node, 'option'):
- name = child.getAttribute('name')
- if not name:
- print _('Skipping unnamed option')
- continue
- vtype = child.getAttribute('type') or 'string'
- if vtype in ('email_list', 'email_list_ex', 'checkbox'):
- value = []
- for subnode in nodegen(child):
- value.append(nodetext(subnode))
- elif vtype == 'bool':
- value = nodetext(child)
- try:
- value = bool(int(value))
- except ValueError:
- value = {'true' : True,
- 'false': False,
- }.get(value.lower())
- if value is None:
- print _('Skipping bad boolean value: $value')
- continue
- elif vtype == 'radio':
- value = nodetext(child).lower()
- boolval = {'true' : True,
- 'false': False,
- }.get(value)
- if boolval is None:
- value = int(value)
- else:
- value = boolval
- elif vtype == 'number':
- value = nodetext(child)
- # First try int then float
- try:
- value = int(value)
- except ValueError:
- value = float(value)
- elif vtype in ('header_filter', 'topics'):
- value = []
- fakebltins = dict(__builtins__ = dict(True=True, False=False))
- for subnode in nodegen(child):
- reprstr = nodetext(subnode)
- # Turn the reprs back into tuples, in a safe way
- tupleval = eval(reprstr, fakebltins)
- value.append(tupleval)
- else:
- value = nodetext(child)
- # And now some special casing :(
- if name == 'new_member_options':
- value = int(nodetext(child))
- config[name] = value
- return config
-
-
-
-
-def parse_roster(node):
- members = []
- for child in nodegen(node, 'member'):
- member = dict()
- member['id'] = mid = child.getAttribute('id')
- if not mid:
- print _('Skipping member with no id')
- continue
- if OPTS.verbose:
- print _('* Processing member: $mid')
- for subnode in nodegen(child):
- attr = subnode.tagName
- if attr == 'delivery':
- value = (subnode.getAttribute('status'),
- subnode.getAttribute('delivery'))
- elif attr in ('hide', 'ack', 'notmetoo', 'nodupes', 'nomail'):
- value = {'true' : True,
- 'false': False,
- }.get(nodetext(subnode).lower(), False)
- elif attr == 'topics':
- value = []
- for subsubnode in nodegen(subnode):
- value.append(nodetext(subsubnode))
- elif attr == 'password':
- value = nodetext(subnode)
- if OPTS.reset_passwords or value == '{NONE}' or not value:
- value = passwords.make_secret(Utils.MakeRandomPassword())
- else:
- value = nodetext(subnode)
- member[attr] = value
- members.append(member)
- return members
-
-
-
-def load(fp):
- try:
- doc = minidom.parse(fp)
- except ExpatError:
- print _('Expat error in file: $fp.name')
- traceback.print_exc()
- sys.exit(1)
- doc.normalize()
- # Make sure there's only one top-level <mailman> node
- gen = nodegen(doc, 'mailman')
- top = gen.next()
- try:
- gen.next()
- except StopIteration:
- pass
- else:
- print _('Malformed XML; duplicate <mailman> nodes')
- sys.exit(1)
- all_listdata = []
- for listnode in nodegen(top, 'list'):
- listdata = dict()
- name = listnode.getAttribute('name')
- if OPTS.verbose:
- print _('Processing list: $name')
- if not name:
- print _('Ignoring malformed <list> node')
- continue
- for child in nodegen(listnode, 'configuration', 'roster'):
- if child.tagName == 'configuration':
- list_config = parse_config(child)
- else:
- assert(child.tagName == 'roster')
- list_roster = parse_roster(child)
- all_listdata.append((name, list_config, list_roster))
- return all_listdata
-
-
-
-def create(all_listdata):
- for name, list_config, list_roster in all_listdata:
- fqdn_listname = '%s@%s' % (name, list_config['host_name'])
- if Utils.list_exists(fqdn_listname):
- print _('Skipping already existing list: $fqdn_listname')
- continue
- mlist = MailList()
- try:
- if OPTS.verbose:
- print _('Creating mailing list: $fqdn_listname')
- mlist.Create(fqdn_listname, list_config['owner'][0],
- list_config['password'])
- except errors.BadDomainSpecificationError:
- print _('List is not in a supported domain: $fqdn_listname')
- continue
- # Save the list creation, then unlock and relock the list. This is so
- # that we use normal SQLAlchemy transactions to manage all the
- # attribute and membership updates. Without this, no transaction will
- # get committed in the second Save() below and we'll lose all our
- # updates.
- mlist.Save()
- mlist.Unlock()
- mlist.Lock()
- try:
- for option, value in list_config.items():
- # XXX Here's what sucks. Some properties need to have
- # _setValue() called on the gui component, because those
- # methods do some pre-processing on the values before they're
- # applied to the MailList instance. But we don't have a good
- # way to find a category and sub-category that a particular
- # property belongs to. Plus this will probably change. So
- # for now, we'll just hard code the extra post-processing
- # here. The good news is that not all _setValue() munging
- # needs to be done -- for example, we've already converted
- # everything to dollar strings.
- if option in ('filter_mime_types', 'pass_mime_types',
- 'filter_filename_extensions',
- 'pass_filename_extensions'):
- value = value.splitlines()
- if option == 'available_languages':
- mlist.set_languages(*value)
- else:
- setattr(mlist, option, value)
- for member in list_roster:
- mid = member['id']
- if OPTS.verbose:
- print _('* Adding member: $mid')
- status, delivery = member['delivery']
- kws = {'password' : member['password'],
- 'language' : member['language'],
- 'realname' : member['realname'],
- 'digest' : delivery <> 'regular',
- }
- mlist.addNewMember(mid, **kws)
- status = {'enabled' : MemberAdaptor.ENABLED,
- 'byuser' : MemberAdaptor.BYUSER,
- 'byadmin' : MemberAdaptor.BYADMIN,
- 'bybounce' : MemberAdaptor.BYBOUNCE,
- }.get(status, MemberAdaptor.UNKNOWN)
- mlist.setDeliveryStatus(mid, status)
- for opt in ('hide', 'ack', 'notmetoo', 'nodupes', 'nomail'):
- mlist.setMemberOption(mid,
- Defaults.OPTINFO[opt],
- member[opt])
- topics = member.get('topics')
- if topics:
- mlist.setMemberTopics(mid, topics)
- mlist.Save()
- finally:
- mlist.Unlock()
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-%prog [options]
-
-Import the configuration and/or members of a mailing list in XML format. The
-imported mailing list must not already exist. All mailing lists named in the
-XML file are imported, but those that already exist are skipped unless --error
-is given."""))
- parser.add_option('-i', '--inputfile',
- metavar='FILENAME', default=None, type='string',
- help=_("""\
-Input XML from FILENAME. If not given, or if FILENAME is '-', standard input
-is used."""))
- parser.add_option('-p', '--reset-passwords',
- default=False, action='store_true', help=_("""\
-With this option, user passwords in the XML are ignored and are reset to a
-random password. If the generated passwords were not included in the input
-XML, they will always be randomly generated."""))
- parser.add_option('-v', '--verbose',
- default=False, action='store_true',
- help=_('Produce more verbose output'))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- if args:
- parser.print_help()
- parser.error(_('Unexpected arguments'))
- return parser, opts, args
-
-
-
-def main():
- global OPTS
-
- parser, opts, args = parseargs()
- initialize(opts.config)
- OPTS = opts
-
- if opts.inputfile in (None, '-'):
- fp = sys.stdin
- else:
- fp = open(opts.inputfile, 'r')
-
- try:
- listbags = load(fp)
- create(listbags)
- finally:
- if fp is not sys.stdin:
- fp.close()
diff --git a/mailman/bin/inject.py b/mailman/bin/inject.py
deleted file mode 100644
index 2bc8a49e3..000000000
--- a/mailman/bin/inject.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# Copyright (C) 2002-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/>.
-
-import os
-import sys
-
-from email import message_from_string
-
-from mailman import Utils
-from mailman.Message import Message
-from mailman.configuration import config
-from mailman.i18n import _
-from mailman.inject import inject_text
-from mailman.options import SingleMailingListOptions
-
-
-
-class ScriptOptions(SingleMailingListOptions):
- usage=_("""\
-%prog [options] [filename]
-
-Inject a message from a file into Mailman's incoming queue. 'filename' is the
-name of the plaintext message file to inject. If omitted, or the string '-',
-standard input is used.
-""")
-
- def add_options(self):
- super(ScriptOptions, self).add_options()
- self.parser.add_option(
- '-q', '--queue',
- type='string', help=_("""\
-The name of the queue to inject the message to. The queuename must be one of
-the directories inside the qfiles directory. If omitted, the incoming queue
-is used."""))
-
- def sanity_check(self):
- if not self.options.listname:
- self.parser.error(_('Missing listname'))
- if len(self.arguments) == 0:
- self.filename = '-'
- elif len(self.arguments) > 1:
- self.parser.print_error(_('Unexpected arguments'))
- else:
- self.filename = self.arguments[0]
-
-
-
-def main():
- options = ScriptOptions()
- options.initialize()
-
- if options.options.queue is None:
- qdir = config.INQUEUE_DIR
- else:
- qdir = os.path.join(config.QUEUE_DIR, options.options.queue)
- if not os.path.isdir(qdir):
- options.parser.error(_('Bad queue directory: $qdir'))
-
- fqdn_listname = options.options.listname
- mlist = config.db.list_manager.get(fqdn_listname)
- if mlist is None:
- options.parser.error(_('No such list: $fqdn_listname'))
-
- if options.filename == '-':
- message_text = sys.stdin.read()
- else:
- with open(options.filename) as fp:
- message_text = fp.read()
-
- inject_text(mlist, message_text, qdir=qdir)
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/list_lists.py b/mailman/bin/list_lists.py
deleted file mode 100644
index ea1640910..000000000
--- a/mailman/bin/list_lists.py
+++ /dev/null
@@ -1,104 +0,0 @@
-# Copyright (C) 1998-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/>.
-
-from mailman.config import config
-from mailman.i18n import _
-from mailman.options import Options
-
-
-
-class ScriptOptions(Options):
- usage = _("""\
-%prog [options]
-
-List all mailing lists.""")
-
- def add_options(self):
- super(ScriptOptions, self).add_options()
- self.parser.add_option(
- '-a', '--advertised',
- default=False, action='store_true',
- help=_("""\
-List only those mailing lists that are publicly advertised"""))
- self.parser.add_option(
- '-b', '--bare',
- default=False, action='store_true',
- help=_("""\
-Displays only the list name, with no description."""))
- self.parser.add_option(
- '-d', '--domain',
- default=[], type='string', action='append',
- dest='domains', help=_("""\
-List only those mailing lists that match the given virtual domain, which may
-be either the email host or the url host name. Multiple -d options may be
-given."""))
- self.parser.add_option(
- '-f', '--full',
- default=False, action='store_true',
- help=_("""\
-Print the full list name, including the posting address."""))
-
- def sanity_check(self):
- if len(self.arguments) > 0:
- self.parser.error(_('Unexpected arguments'))
-
-
-
-def main():
- options = ScriptOptions()
- options.initialize()
-
- mlists = []
- longest = 0
-
- listmgr = config.db.list_manager
- for fqdn_name in sorted(listmgr.names):
- mlist = listmgr.get(fqdn_name)
- if options.options.advertised and not mlist.advertised:
- continue
- if options.options.domains:
- for domain in options.options.domains:
- if domain in mlist.web_page_url or domain == mlist.host_name:
- mlists.append(mlist)
- break
- else:
- mlists.append(mlist)
- if options.options.full:
- name = mlist.fqdn_listname
- else:
- name = mlist.real_name
- longest = max(len(name), longest)
-
- if not mlists and not options.options.bare:
- print _('No matching mailing lists found')
- return
-
- if not options.options.bare:
- num_mlists = len(mlists)
- print _('$num_mlists matching mailing lists found:')
-
- format = '%%%ds - %%.%ds' % (longest, 77 - longest)
- for mlist in mlists:
- if options.options.full:
- name = mlist.fqdn_listname
- else:
- name = mlist.real_name
- if options.options.bare:
- print name
- else:
- description = mlist.description or _('[no description available]')
- print ' ', format % (name, description)
diff --git a/mailman/bin/list_members.py b/mailman/bin/list_members.py
deleted file mode 100644
index 443f764d6..000000000
--- a/mailman/bin/list_members.py
+++ /dev/null
@@ -1,201 +0,0 @@
-# Copyright (C) 1998-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/>.
-
-import sys
-
-from email.Utils import formataddr
-
-from mailman import Utils
-from mailman.config import config
-from mailman.core import errors
-from mailman.i18n import _
-from mailman.interfaces import DeliveryStatus
-from mailman.options import SingleMailingListOptions
-
-
-COMMASPACE = ', '
-
-WHYCHOICES = {
- 'enabled' : DeliveryStatus.enabled,
- 'byuser' : DeliveryStatus.by_user,
- 'byadmin' : DeliveryStatus.by_moderator,
- 'bybounce': DeliveryStatus.by_bounces,
- }
-
-KINDCHOICES = set(('mime', 'plain', 'any'))
-
-
-
-class ScriptOptions(SingleMailingListOptions):
- usage = _("""\
-%prog [options]
-
-List all the members of a mailing list. Note that with the options below, if
-neither -r or -d is supplied, regular members are printed first, followed by
-digest members, but no indication is given as to address status.
-
-listname is the name of the mailing list to use.""")
-
- def add_options(self):
- super(ScriptOptions, self).add_options()
- self.parser.add_option(
- '-o', '--output',
- type='string', help=_("""\
-Write output to specified file instead of standard out."""))
- self.parser.add_option(
- '-r', '--regular',
- default=None, action='store_true',
- help=_('Print just the regular (non-digest) members.'))
- self.parser.add_option(
- '-d', '--digest',
- default=None, type='string', metavar='KIND',
- help=_("""\
-Print just the digest members. KIND can be 'mime', 'plain', or
-'any'. 'mime' prints just the members receiving MIME digests, while 'plain'
-prints just the members receiving plain text digests. 'any' prints all
-members receiving any kind of digest."""))
- self.parser.add_option(
- '-n', '--nomail',
- type='string', metavar='WHY', help=_("""\
-Print the members that have delivery disabled. WHY selects just the subset of
-members with delivery disabled for a particular reason, where 'any' prints all
-disabled members. 'byadmin', 'byuser', 'bybounce', and 'unknown' prints just
-the users who are disabled for that particular reason. WHY can also be
-'enabled' which prints just those members for whom delivery is enabled."""))
- self.parser.add_option(
- '-f', '--fullnames',
- default=False, action='store_true',
- help=_('Include the full names in the output'))
- self.parser.add_option(
- '-i', '--invalid',
- default=False, action='store_true', help=_("""\
-Print only the addresses in the membership list that are invalid. Ignores -r,
--d, -n."""))
-
- def sanity_check(self):
- if not self.options.listname:
- self.parser.error(_('Missing listname'))
- if len(self.arguments) > 0:
- self.parser.print_error(_('Unexpected arguments'))
- if self.options.digest is not None:
- self.options.kind = self.options.digest.lower()
- if self.options.kind not in KINDCHOICES:
- self.parser.error(
- _('Invalid value for -d: $self.options.digest'))
- if self.options.nomail is not None:
- why = self.options.nomail.lower()
- if why == 'any':
- self.options.why = 'any'
- elif why not in WHYCHOICES:
- self.parser.error(
- _('Invalid value for -n: $self.options.nomail'))
- self.options.why = why
- if self.options.regular is None and self.options.digest is None:
- self.options.regular = self.options.digest = True
- self.options.kind = 'any'
-
-
-
-def safe(string):
- if not string:
- return ''
- return string.encode(sys.getdefaultencoding(), 'replace')
-
-
-def isinvalid(addr):
- try:
- Utils.ValidateEmail(addr)
- return False
- except errors.EmailAddressError:
- return True
-
-
-
-def whymatches(mlist, addr, why):
- # Return true if the `why' matches the reason the address is enabled, or
- # in the case of why is None, that they are disabled for any reason
- # (i.e. not enabled).
- status = mlist.getDeliveryStatus(addr)
- if why in (None, 'any'):
- return status <> DeliveryStatus.enabled
- return status == WHYCHOICES[why]
-
-
-
-def main():
- options = ScriptOptions()
- options.initialize()
-
- fqdn_listname = options.options.listname
- if options.options.output:
- try:
- fp = open(options.output, 'w')
- except IOError:
- options.parser.error(
- _('Could not open file for writing: $options.options.output'))
- else:
- fp = sys.stdout
-
- mlist = config.db.list_manager.get(fqdn_listname)
- if mlist is None:
- options.parser.error(_('No such list: $fqdn_listname'))
-
- # The regular delivery and digest members.
- rmembers = set(mlist.regular_members.members)
- dmembers = set(mlist.digest_members.members)
-
- fullnames = options.options.fullnames
- if options.options.invalid:
- all = sorted(member.address.address for member in rmembers + dmembers)
- for address in all:
- user = config.db.user_manager.get_user(address)
- name = (user.real_name if fullnames and user else u'')
- if options.options.invalid and isinvalid(address):
- print >> fp, formataddr((safe(name), address))
- return
- if options.options.regular:
- for address in sorted(member.address.address for member in rmembers):
- user = config.db.user_manager.get_user(address)
- name = (user.real_name if fullnames and user else u'')
- # Filter out nomails
- if (options.options.nomail and
- not whymatches(mlist, address, options.options.why)):
- continue
- print >> fp, formataddr((safe(name), address))
- if options.options.digest:
- for address in sorted(member.address.address for member in dmembers):
- user = config.db.user_manager.get_user(address)
- name = (user.real_name if fullnames and user else u'')
- # Filter out nomails
- if (options.options.nomail and
- not whymatches(mlist, address, options.options.why)):
- continue
- # Filter out digest kinds
-## if mlist.getMemberOption(addr, config.DisableMime):
-## # They're getting plain text digests
-## if opts.kind == 'mime':
-## continue
-## else:
-## # They're getting MIME digests
-## if opts.kind == 'plain':
-## continue
- print >> fp, formataddr((safe(name), address))
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/list_owners.py b/mailman/bin/list_owners.py
deleted file mode 100644
index 953fb8941..000000000
--- a/mailman/bin/list_owners.py
+++ /dev/null
@@ -1,88 +0,0 @@
-# Copyright (C) 2002-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/>.
-
-import sys
-import optparse
-
-from mailman.MailList import MailList
-from mailman.configuration import config
-from mailman.i18n import _
-from mailman.initialize import initialize
-from mailman.version import MAILMAN_VERSION
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-%prog [options] [listname ...]
-
-List the owners of a mailing list, or all mailing lists if no list names are
-given."""))
- parser.add_option('-w', '--with-listnames',
- default=False, action='store_true',
- help=_("""\
-Group the owners by list names and include the list names in the output.
-Otherwise, the owners will be sorted and uniquified based on the email
-address."""))
- parser.add_option('-m', '--moderators',
- default=False, action='store_true',
- help=_('Include the list moderators in the output.'))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- return parser, opts, args
-
-
-
-def main():
- parser, opts, args = parseargs()
- initialize(opts.config)
-
- listmgr = config.db.list_manager
- listnames = set(args or listmgr.names)
- bylist = {}
-
- for listname in listnames:
- mlist = listmgr.get(listname)
- addrs = [addr.address for addr in mlist.owners.addresses]
- if opts.moderators:
- addrs.extend([addr.address for addr in mlist.moderators.addresses])
- bylist[listname] = addrs
-
- if opts.with_listnames:
- for listname in listnames:
- unique = set()
- for addr in bylist[listname]:
- unique.add(addr)
- keys = list(unique)
- keys.sort()
- print listname
- for k in keys:
- print '\t', k
- else:
- unique = set()
- for listname in listnames:
- for addr in bylist[listname]:
- unique.add(addr)
- for k in sorted(unique):
- print k
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/mailmanctl.py b/mailman/bin/mailmanctl.py
deleted file mode 100644
index 667a46a70..000000000
--- a/mailman/bin/mailmanctl.py
+++ /dev/null
@@ -1,232 +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/>.
-
-"""Mailman start/stop script."""
-
-import os
-import grp
-import pwd
-import sys
-import errno
-import signal
-import logging
-
-from optparse import OptionParser
-
-from mailman.config import config
-from mailman.core.initialize import initialize
-from mailman.i18n import _
-from mailman.version import MAILMAN_VERSION
-
-
-COMMASPACE = ', '
-
-log = None
-parser = None
-
-
-
-def parseargs():
- parser = OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-Primary start-up and shutdown script for Mailman's qrunner daemon.
-
-This script starts, stops, and restarts the main Mailman queue runners, making
-sure that the various long-running qrunners are still alive and kicking. It
-does this by forking and exec'ing the qrunners and waiting on their pids.
-When it detects a subprocess has exited, it may restart it.
-
-The qrunners respond to SIGINT, SIGTERM, SIGUSR1 and SIGHUP. SIGINT, SIGTERM
-and SIGUSR1 all cause the qrunners to exit cleanly, but the master will only
-restart qrunners that have exited due to a SIGUSR1. SIGHUP causes the master
-and the qrunners to close their log files, and reopen then upon the next
-printed message.
-
-The master also responds to SIGINT, SIGTERM, SIGUSR1 and SIGHUP, which it
-simply passes on to the qrunners (note that the master will close and reopen
-its own log files on receipt of a SIGHUP). The master also leaves its own
-process id in the file data/master-qrunner.pid but you normally don't need to
-use this pid directly. The `start', `stop', `restart', and `reopen' commands
-handle everything for you.
-
-Commands:
-
- start - Start the master daemon and all qrunners. Prints a message and
- exits if the master daemon is already running.
-
- stop - Stops the master daemon and all qrunners. After stopping, no
- more messages will be processed.
-
- restart - Restarts the qrunners, but not the master process. Use this
- whenever you upgrade or update Mailman so that the qrunners will
- use the newly installed code.
-
- reopen - This will close all log files, causing them to be re-opened the
- next time a message is written to them
-
-Usage: %prog [options] [ start | stop | restart | reopen ]"""))
- parser.add_option('-u', '--run-as-user',
- default=True, action='store_false',
- help=_("""\
-Normally, this script will refuse to run if the user id and group id are not
-set to the `mailman' user and group (as defined when you configured Mailman).
-If run as root, this script will change to this user and group before the
-check is made.
-
-This can be inconvenient for testing and debugging purposes, so the -u flag
-means that the step that sets and checks the uid/gid is skipped, and the
-program is run as the current user and group. This flag is not recommended
-for normal production environments.
-
-Note though, that if you run with -u and are not in the mailman group, you may
-have permission problems, such as begin unable to delete a list's archives
-through the web. Tough luck!"""))
- parser.add_option('-f', '--force',
- default=False, action='store_true',
- help=_("""\
-If the master watcher finds an existing master lock, it will normally exit
-with an error message. With this option,the master will perform an extra
-level of checking. If a process matching the host/pid described in the lock
-file is running, the master will still exit, requiring you to manually clean
-up the lock. But if no matching process is found, the master will remove the
-apparently stale lock and make another attempt to claim the master lock."""))
- parser.add_option('-q', '--quiet',
- default=False, action='store_true',
- help=_("""\
-Don't print status messages. Error messages are still printed to standard
-error."""))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- options, arguments = parser.parse_args()
- if not arguments:
- parser.error(_('No command given.'))
- if len(arguments) > 1:
- commands = COMMASPACE.join(arguments)
- parser.error(_('Bad command: $commands'))
- parser.options = options
- parser.arguments = arguments
- return parser
-
-
-
-def kill_watcher(sig):
- try:
- with open(config.PIDFILE) as f:
- pid = int(f.read().strip())
- except (IOError, ValueError), e:
- # For i18n convenience
- print >> sys.stderr, _('PID unreadable in: $config.PIDFILE')
- print >> sys.stderr, e
- print >> sys.stderr, _('Is qrunner even running?')
- return
- try:
- os.kill(pid, sig)
- except OSError, error:
- if e.errno <> errno.ESRCH:
- raise
- print >> sys.stderr, _('No child with pid: $pid')
- print >> sys.stderr, e
- print >> sys.stderr, _('Stale pid file removed.')
- os.unlink(config.PIDFILE)
-
-
-
-def check_privileges():
- # If we're running as root (uid == 0), coerce the uid and gid to that
- # which Mailman was configured for, and refuse to run if we didn't coerce
- # the uid/gid.
- gid = grp.getgrnam(config.MAILMAN_GROUP).gr_gid
- uid = pwd.getpwnam(config.MAILMAN_USER).pw_uid
- myuid = os.getuid()
- if myuid == 0:
- # Set the process's supplimental groups.
- groups = [group.gr_gid for group in grp.getgrall()
- if config.MAILMAN_USER in group.gr_mem]
- groups.append(gid)
- os.setgroups(groups)
- os.setgid(gid)
- os.setuid(uid)
- elif myuid <> uid:
- name = config.MAILMAN_USER
- parser.error(
- _('Run this program as root or as the $name user, or use -u.'))
-
-
-
-def main():
- global log, parser
-
- parser = parseargs()
- initialize(parser.options.config)
-
- log = logging.getLogger('mailman.qrunner')
-
- if not parser.options.run_as_user:
- check_privileges()
- else:
- if not parser.options.quiet:
- print _('Warning! You may encounter permission problems.')
-
- # Handle the commands
- command = parser.arguments[0].lower()
- if command == 'stop':
- if not parser.options.quiet:
- print _("Shutting down Mailman's master qrunner")
- kill_watcher(signal.SIGTERM)
- elif command == 'restart':
- if not parser.options.quiet:
- print _("Restarting Mailman's master qrunner")
- kill_watcher(signal.SIGUSR1)
- elif command == 'reopen':
- if not parser.options.quiet:
- print _('Re-opening all log files')
- kill_watcher(signal.SIGHUP)
- elif command == 'start':
- # Start the master qrunner watcher process.
- #
- # Daemon process startup according to Stevens, Advanced Programming in
- # the UNIX Environment, Chapter 13.
- pid = os.fork()
- if pid:
- # parent
- if not parser.options.quiet:
- print _("Starting Mailman's master qrunner.")
- return
- # child
- #
- # 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 runtime directory.
- os.chdir(config.VAR_DIR)
- # Exec the master watcher.
- args = [sys.executable, sys.executable,
- os.path.join(config.BIN_DIR, 'master')]
- if parser.options.force:
- args.append('--force')
- if parser.options.config:
- args.extend(['-C', parser.options.config])
- log.debug('starting: %s', args)
- os.execl(*args)
- # We should never get here.
- raise RuntimeError('os.execl() failed')
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/master.py b/mailman/bin/master.py
deleted file mode 100644
index d954bc865..000000000
--- a/mailman/bin/master.py
+++ /dev/null
@@ -1,452 +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/>.
-
-"""Master sub-process watcher."""
-
-__metaclass__ = type
-__all__ = [
- 'Loop',
- 'get_lock_data',
- ]
-
-
-import os
-import sys
-import errno
-import signal
-import socket
-import logging
-
-from datetime import timedelta
-from lazr.config import as_boolean
-from locknix import lockfile
-from munepy import Enum
-
-from mailman.config import config
-from mailman.core.logging import reopen
-from mailman.i18n import _
-from mailman.options import Options
-
-
-DOT = '.'
-LOCK_LIFETIME = timedelta(days=1, hours=6)
-SECONDS_IN_A_DAY = 86400
-
-
-
-class ScriptOptions(Options):
- """Options for the master watcher."""
-
- usage = _("""\
-Master sub-process watcher.
-
-Start and watch the configured queue runners and ensure that they stay alive
-and kicking. Each are fork and exec'd in turn, with the master waiting on
-their process ids. When it detects a child queue runner has exited, it may
-restart it.
-
-The queue runners respond to SIGINT, SIGTERM, SIGUSR1 and SIGHUP. SIGINT,
-SIGTERM and SIGUSR1 all cause the qrunners to exit cleanly. The master will
-restart qrunners that have exited due to a SIGUSR1 or some kind of other exit
-condition (say because of an exception). SIGHUP causes the master and the
-qrunners to close their log files, and reopen then upon the next printed
-message.
-
-The master also responds to SIGINT, SIGTERM, SIGUSR1 and SIGHUP, which it
-simply passes on to the qrunners. Note that the master will close and reopen
-its own log files on receipt of a SIGHUP. The master also leaves its own
-process id in the file `data/master-qrunner.pid` but you normally don't need
-to use this pid directly.
-
-Usage: %prog [options]""")
-
- def add_options(self):
- self.parser.add_option(
- '-n', '--no-restart',
- dest='restartable', default=True, action='store_false',
- help=_("""\
-Don't restart the qrunners when they exit because of an error or a SIGUSR1.
-Use this only for debugging."""))
- self.parser.add_option(
- '-f', '--force',
- default=False, action='store_true',
- help=_("""\
-If the master watcher finds an existing master lock, it will normally exit
-with an error message. With this option,the master will perform an extra
-level of checking. If a process matching the host/pid described in the lock
-file is running, the master will still exit, requiring you to manually clean
-up the lock. But if no matching process is found, the master will remove the
-apparently stale lock and make another attempt to claim the master lock."""))
- self.parser.add_option(
- '-r', '--runner',
- dest='runners', action='append', default=[],
- help=_("""\
-Override the default set of queue runners that the master watch will invoke
-instead of the default set. Multiple -r options may be given. The values for
--r are passed straight through to bin/qrunner."""))
-
- def sanity_check(self):
- if len(self.arguments) > 0:
- self.parser.error(_('Too many arguments'))
-
-
-
-def get_lock_data():
- """Get information from the master lock file.
-
- :return: A 3-tuple of the hostname, integer process id, and file name of
- the lock file.
- """
- with open(config.LOCK_FILE) as fp:
- filename = os.path.split(fp.read().strip())[1]
- parts = filename.split('.')
- hostname = DOT.join(parts[1:-2])
- pid = int(parts[-2])
- return hostname, int(pid), filename
-
-
-class WatcherState(Enum):
- # Another master watcher is running.
- conflict = 1
- # No conflicting process exists.
- stale_lock = 2
- # Hostname from lock file doesn't match.
- host_mismatch = 3
-
-
-def master_state():
- """Get the state of the master watcher.
-
- :return: WatcherState describing the state of the lock file.
- """
-
- # 1 if proc exists on host (but is it qrunner? ;)
- # 0 if host matches but no proc
- # hostname if hostname doesn't match
- hostname, pid, tempfile = get_lock_data()
- if hostname <> socket.gethostname():
- return WatcherState.host_mismatch
- # Find out if the process exists by calling kill with a signal 0.
- try:
- os.kill(pid, 0)
- return WatcherState.conflict
- except OSError, e:
- if e.errno == errno.ESRCH:
- # No matching process id.
- return WatcherState.stale_lock
- # Some other error occurred.
- raise
-
-
-def acquire_lock_1(force):
- """Try to acquire the master queue runner lock.
-
- :param force: Flag that controls whether to force acquisition of the lock.
- :return: The master queue runner lock.
- :raises: `TimeOutError` if the lock could not be acquired.
- """
- lock = lockfile.Lock(config.LOCK_FILE, LOCK_LIFETIME)
- try:
- lock.lock(timedelta(seconds=0.1))
- return lock
- except lockfile.TimeOutError:
- if not force:
- raise
- # Force removal of lock first.
- lock.disown()
- hostname, pid, tempfile = get_lock_data()
- os.unlink(config.LOCK_FILE)
- os.unlink(os.path.join(config.LOCK_DIR, tempfile))
- return acquire_lock_1(force=False)
-
-
-def acquire_lock(force):
- """Acquire the master queue runner lock.
-
- :return: The master queue runner lock or None if the lock couldn't be
- acquired. In that case, an error messages is also printed to standard
- error.
- """
- try:
- lock = acquire_lock_1(force)
- return lock
- except lockfile.TimeOutError:
- status = master_state()
- if status == WatcherState.conflict:
- # Hostname matches and process exists.
- message = _("""\
-The master qrunner lock could not be acquired because it appears
-as though another master qrunner is already running.
-""")
- elif status == WatcherState.stale_lock:
- # Hostname matches but the process does not exist.
- message = _("""\
-The master qrunner lock could not be acquired. It appears as though there is
-a stale master qrunner lock. Try re-running mailmanctl with the -s flag.
-""")
- else:
- assert status == WatcherState.host_mismatch, (
- 'Invalid enum value: %s' % status)
- # Hostname doesn't even match.
- hostname, pid, tempfile = get_lock_data()
- message = _("""\
-The master qrunner lock could not be acquired, because it appears as if some
-process on some other host may have acquired it. We can't test for stale
-locks across host boundaries, so you'll have to clean this up manually.
-
-Lock file: $config.LOCK_FILE
-Lock host: $hostname
-
-Exiting.""")
- config.options.parser.error(message)
-
-
-
-class Loop:
- """Main control loop class."""
-
- def __init__(self, lock=None, restartable=None, config_file=None):
- self._lock = lock
- self._restartable = restartable
- self._config_file = config_file
- self._kids = {}
-
- def install_signal_handlers(self):
- """Install various signals handlers for control from mailmanctl."""
- log = logging.getLogger('mailman.qrunner')
- # Set up our signal handlers. Also set up a SIGALRM handler to
- # refresh the lock once per day. The lock lifetime is 1 day + 6 hours
- # so this should be plenty.
- def sigalrm_handler(signum, frame):
- self._lock.refresh()
- signal.alarm(SECONDS_IN_A_DAY)
- signal.signal(signal.SIGALRM, sigalrm_handler)
- signal.alarm(SECONDS_IN_A_DAY)
- # SIGHUP tells the qrunners to close and reopen their log files.
- def sighup_handler(signum, frame):
- reopen()
- for pid in self._kids:
- os.kill(pid, signal.SIGHUP)
- log.info('Master watcher caught SIGHUP. Re-opening log files.')
- signal.signal(signal.SIGHUP, sighup_handler)
- # SIGUSR1 is used by 'mailman restart'.
- def sigusr1_handler(signum, frame):
- for pid in self._kids:
- os.kill(pid, signal.SIGUSR1)
- log.info('Master watcher caught SIGUSR1. Exiting.')
- signal.signal(signal.SIGUSR1, sigusr1_handler)
- # SIGTERM is what init will kill this process with when changing run
- # levels. It's also the signal 'mailmanctl stop' uses.
- def sigterm_handler(signum, frame):
- for pid in self._kids:
- os.kill(pid, signal.SIGTERM)
- log.info('Master watcher caught SIGTERM. Exiting.')
- signal.signal(signal.SIGTERM, sigterm_handler)
- # SIGINT is what control-C gives.
- def sigint_handler(signum, frame):
- for pid in self._kids:
- os.kill(pid, signal.SIGINT)
- log.info('Master watcher caught SIGINT. Restarting.')
- signal.signal(signal.SIGINT, sigint_handler)
-
- def _start_runner(self, spec):
- """Start a queue runner.
-
- All arguments are passed to the qrunner process.
-
- :param spec: A queue runner spec, in a format acceptable to
- bin/qrunner's --runner argument, e.g. name:slice:count
- :type spec: string
- :return: The process id of the child queue runner.
- :rtype: int
- """
- pid = os.fork()
- if pid:
- # Parent.
- return pid
- # Child.
- #
- # Craft the command line arguments for the exec() call.
- rswitch = '--runner=' + spec
- # Wherever mailmanctl lives, so too must live the qrunner script.
- exe = os.path.join(config.BIN_DIR, 'qrunner')
- # config.PYTHON, which is the absolute path to the Python interpreter,
- # must be given as argv[0] due to Python's library search algorithm.
- args = [sys.executable, sys.executable, exe, rswitch, '-s']
- if self._config_file is not None:
- args.extend(['-C', self._config_file])
- log = logging.getLogger('mailman.qrunner')
- log.debug('starting: %s', args)
- os.execl(*args)
- # We should never get here.
- raise RuntimeError('os.execl() failed')
-
- def start_qrunners(self, qrunner_names=None):
- """Start all the configured qrunners.
-
- :param qrunners: If given, a sequence of queue runner names to start.
- If not given, this sequence is taken from the configuration file.
- :type qrunners: a sequence of strings
- """
- if not qrunner_names:
- qrunner_names = []
- for qrunner_config in config.qrunner_configs:
- # Strip off the 'qrunner.' prefix.
- assert qrunner_config.name.startswith('qrunner.'), (
- 'Unexpected qrunner configuration section name: %s',
- qrunner_config.name)
- qrunner_names.append(qrunner_config.name[8:])
- # For each qrunner we want to start, find their config section, which
- # will tell us the name of the class to instantiate, along with the
- # number of hash space slices to manage.
- for name in qrunner_names:
- section_name = 'qrunner.' + name
- # Let AttributeError propagate.
- qrunner_config = getattr(config, section_name)
- if not as_boolean(qrunner_config.start):
- continue
- package, class_name = qrunner_config['class'].rsplit(DOT, 1)
- __import__(package)
- # Let AttributeError propagate.
- class_ = getattr(sys.modules[package], class_name)
- # Find out how many qrunners to instantiate. This must be a power
- # of 2.
- count = int(qrunner_config.instances)
- assert (count & (count - 1)) == 0, (
- 'Queue runner "%s", not a power of 2: %s', name, count)
- for slice_number in range(count):
- # qrunner name, slice #, # of slices, restart count
- info = (name, slice_number, count, 0)
- spec = '%s:%d:%d' % (name, slice_number, count)
- pid = self._start_runner(spec)
- log = logging.getLogger('mailman.qrunner')
- log.debug('[%d] %s', pid, spec)
- self._kids[pid] = info
-
- def loop(self):
- """Main loop.
-
- Wait until all the qrunners have exited, restarting them if necessary
- and configured to do so.
- """
- log = logging.getLogger('mailman.qrunner')
- while True:
- try:
- pid, status = os.wait()
- except OSError, error:
- # No children? We're done.
- if error.errno == errno.ECHILD:
- break
- # If the system call got interrupted, just restart it.
- elif error.errno == errno.EINTR:
- continue
- else:
- raise
- # Find out why the subprocess exited by getting the signal
- # received or exit status.
- if os.WIFSIGNALED(status):
- why = os.WTERMSIG(status)
- elif os.WIFEXITED(status):
- why = os.WEXITSTATUS(status)
- else:
- why = None
- # We'll restart the subprocess if it exited with a SIGUSR1 or
- # because of a failure (i.e. no exit signal), and the no-restart
- # command line switch was not given. This lets us better handle
- # runaway restarts (e.g. if the subprocess had a syntax error!)
- qrname, slice_number, count, restarts = self._kids.pop(pid)
- config_name = 'qrunner.' + qrname
- restart = False
- if why == signal.SIGUSR1 and self._restartable:
- restart = True
- # Have we hit the maximum number of restarts?
- restarts += 1
- max_restarts = int(getattr(config, config_name).max_restarts)
- if restarts > max_restarts:
- restart = False
- # Are we permanently non-restartable?
- log.debug("""\
-Master detected subprocess exit
-(pid: %d, why: %s, class: %s, slice: %d/%d) %s""",
- pid, why, qrname, slice_number + 1, count,
- ('[restarting]' if restart else ''))
- # See if we've reached the maximum number of allowable restarts
- if restarts > max_restarts:
- log.info("""\
-qrunner %s reached maximum restart limit of %d, not restarting.""",
- qrname, max_restarts)
- # Now perhaps restart the process unless it exited with a
- # SIGTERM or we aren't restarting.
- if restart:
- spec = '%s:%d:%d' % (qrname, slice_number, count)
- newpid = self._start_runner(spec)
- self._kids[newpid] = (qrname, slice_number, count, restarts)
-
- def cleanup(self):
- """Ensure that all children have exited."""
- log = logging.getLogger('mailman.qrunner')
- # Send SIGTERMs to all the child processes and wait for them all to
- # exit.
- for pid in self._kids:
- try:
- os.kill(pid, signal.SIGTERM)
- except OSError, error:
- if error.errno == errno.ESRCH:
- # The child has already exited.
- log.info('ESRCH on pid: %d', pid)
- # Wait for all the children to go away.
- while self._kids:
- try:
- pid, status = os.wait()
- del self._kids[pid]
- except OSError, e:
- if e.errno == errno.ECHILD:
- break
- elif e.errno == errno.EINTR:
- continue
- raise
-
-
-
-def main():
- """Main process."""
-
- options = ScriptOptions()
- options.initialize()
-
- # Acquire the master lock, exiting if we can't acquire it. We'll let the
- # caller handle any clean up or lock breaking. No with statement here
- # because Lock's constructor doesn't support a timeout.
- lock = acquire_lock(options.options.force)
- try:
- with open(config.PIDFILE, 'w') as fp:
- print >> fp, os.getpid()
- loop = Loop(lock, options.options.restartable, options.options.config)
- loop.install_signal_handlers()
- try:
- loop.start_qrunners(options.options.runners)
- loop.loop()
- finally:
- loop.cleanup()
- os.remove(config.PIDFILE)
- finally:
- lock.unlock()
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/mmsitepass.py b/mailman/bin/mmsitepass.py
deleted file mode 100644
index 132803fc9..000000000
--- a/mailman/bin/mmsitepass.py
+++ /dev/null
@@ -1,113 +0,0 @@
-# Copyright (C) 1998-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/>.
-
-import sys
-import getpass
-import optparse
-
-from mailman import Utils
-from mailman import passwords
-from mailman.configuration import config
-from mailman.i18n import _
-from mailman.initialize import initialize
-from mailman.version import MAILMAN_VERSION
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-%prog [options] [password]
-
-Set the site or list creator password.
-
-The site password can be used in most if not all places that the list
-administrator's password can be used, which in turn can be used in most places
-that a list user's password can be used. The list creator password is a
-separate password that can be given to non-site administrators to delegate the
-ability to create new mailing lists.
-
-If password is not given on the command line, it will be prompted for.
-"""))
- parser.add_option('-c', '--listcreator',
- default=False, action='store_true',
- help=_("""\
-Set the list creator password instead of the site password. The list
-creator is authorized to create and remove lists, but does not have
-the total power of the site administrator."""))
- parser.add_option('-p', '--password-scheme',
- default='', type='string',
- help=_("""\
-Specify the RFC 2307 style hashing scheme for passwords included in the
-output. Use -P to get a list of supported schemes, which are
-case-insensitive."""))
- parser.add_option('-P', '--list-hash-schemes',
- default=False, action='store_true', help=_("""\
-List the supported password hashing schemes and exit. The scheme labels are
-case-insensitive."""))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- if len(args) > 1:
- parser.error(_('Unexpected arguments'))
- if opts.list_hash_schemes:
- for label in passwords.Schemes:
- print str(label).upper()
- sys.exit(0)
- return parser, opts, args
-
-
-def check_password_scheme(parser, password_scheme):
- # shoule be checked after config is loaded.
- if password_scheme == '':
- password_scheme = config.PASSWORD_SCHEME
- scheme = passwords.lookup_scheme(password_scheme.lower())
- if not scheme:
- parser.error(_('Invalid password scheme'))
- return scheme
-
-
-
-def main():
- parser, opts, args = parseargs()
- initialize(opts.config)
- opts.password_scheme = check_password_scheme(parser, opts.password_scheme)
- if args:
- password = args[0]
- else:
- # Prompt for the password
- if opts.listcreator:
- prompt_1 = _('New list creator password: ')
- else:
- prompt_1 = _('New site administrator password: ')
- pw1 = getpass.getpass(prompt_1)
- pw2 = getpass.getpass(_('Enter password again to confirm: '))
- if pw1 <> pw2:
- print _('Passwords do not match; no changes made.')
- sys.exit(1)
- password = pw1
- Utils.set_global_password(password,
- not opts.listcreator, opts.password_scheme)
- if Utils.check_global_password(password, not opts.listcreator):
- print _('Password changed.')
- else:
- print _('Password change failed.')
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/nightly_gzip.py b/mailman/bin/nightly_gzip.py
deleted file mode 100644
index f886e5801..000000000
--- a/mailman/bin/nightly_gzip.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# Copyright (C) 1998-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/>.
-
-import os
-import sys
-import optparse
-
-try:
- import gzip
-except ImportError:
- sys.exit(0)
-
-from mailman import MailList
-from mailman.configuration import config
-from mailman.i18n import _
-from mailman.initialize import initialize
-from mailman.version import MAILMAN_VERSION
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-%prog [options] [listname ...]
-
-Re-generate the Pipermail gzip'd archive flat files."""))
- parser.add_option('-v', '--verbose',
- default=False, action='store_true',
- help=_("Print each file as it's being gzip'd"))
- parser.add_option('-z', '--level',
- default=6, type='int',
- help=_('Specifies the compression level'))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- if opts.level < 1 or opts.level > 9:
- parser.print_help()
- print >> sys.stderr, _('Illegal compression level: $opts.level')
- sys.exit(1)
- return opts, args, parser
-
-
-
-def compress(txtfile, opts):
- if opts.verbose:
- print _("gzip'ing: $txtfile")
- infp = outfp = None
- try:
- infp = open(txtfile)
- outfp = gzip.open(txtfile + '.gz', 'wb', opts.level)
- outfp.write(infp.read())
- finally:
- if outfp:
- outfp.close()
- if infp:
- infp.close()
-
-
-
-def main():
- opts, args, parser = parseargs()
- 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
- # archived to the mbox, and the gzip file is not created on demand
- # (i.e. for every individual post). This is the normal mode of
- # operation.
- return
-
- # Process all the specified lists
- for listname in set(args or config.list_manager.names):
- mlist = MailList.MailList(listname, lock=False)
- if not mlist.archive:
- continue
- dir = mlist.archive_dir()
- try:
- allfiles = os.listdir(dir)
- except OSError:
- # Has the list received any messages? If not, last_post_time will
- # be zero, so it's not really a bogus archive dir.
- if mlist.last_post_time > 0:
- print _('List $listname has a bogus archive_directory: $dir')
- continue
- if opts.verbose:
- print _('Processing list: $listname')
- files = []
- for f in allfiles:
- if os.path.splitext(f)[1] <> '.txt':
- continue
- # stat both the .txt and .txt.gz files and append them only if
- # the former is newer than the latter.
- txtfile = os.path.join(dir, f)
- gzpfile = txtfile + '.gz'
- txt_mtime = os.path.getmtime(txtfile)
- try:
- gzp_mtime = os.path.getmtime(gzpfile)
- except OSError:
- gzp_mtime = -1
- if txt_mtime > gzp_mtime:
- files.append(txtfile)
- for f in files:
- compress(f, opts)
diff --git a/mailman/bin/qrunner.py b/mailman/bin/qrunner.py
deleted file mode 100644
index 62e943aad..000000000
--- a/mailman/bin/qrunner.py
+++ /dev/null
@@ -1,269 +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/>.
-
-import sys
-import signal
-import logging
-
-from mailman.config import config
-from mailman.core.logging import reopen
-from mailman.i18n import _
-from mailman.options import Options
-
-
-COMMASPACE = ', '
-log = None
-
-
-
-def r_callback(option, opt, value, parser):
- dest = getattr(parser.values, option.dest)
- parts = value.split(':')
- if len(parts) == 1:
- runner = parts[0]
- rslice = rrange = 1
- elif len(parts) == 3:
- runner = parts[0]
- try:
- rslice = int(parts[1])
- rrange = int(parts[2])
- except ValueError:
- parser.print_help()
- print >> sys.stderr, _('Bad runner specification: $value')
- sys.exit(1)
- else:
- parser.print_help()
- print >> sys.stderr, _('Bad runner specification: $value')
- sys.exit(1)
- dest.append((runner, rslice, rrange))
-
-
-
-class ScriptOptions(Options):
-
- usage = _("""\
-Run one or more qrunners, once or repeatedly.
-
-Each named runner class is run in round-robin fashion. In other words, the
-first named runner is run to consume all the files currently in its
-directory. When that qrunner is done, the next one is run to consume all the
-files in /its/ directory, and so on. The number of total iterations can be
-given on the command line.
-
-Usage: %prog [options]
-
--r is required unless -l or -h is given, and its argument must be one of the
-names displayed by the -l switch.
-
-Normally, this script should be started from mailmanctl. Running it
-separately or with -o is generally useful only for debugging.
-""")
-
- def add_options(self):
- self.parser.add_option(
- '-r', '--runner',
- metavar='runner[:slice:range]', dest='runners',
- type='string', default=[],
- action='callback', callback=r_callback,
- help=_("""\
-Run the named qrunner, which must be one of the strings returned by the -l
-option. Optional slice:range if given, is used to assign multiple qrunner
-processes to a queue. range is the total number of qrunners for this queue
-while slice is the number of this qrunner from [0..range).
-
-When using the slice:range form, you must ensure that each qrunner for the
-queue is given the same range value. If slice:runner is not given, then 1:1
-is used.
-
-Multiple -r options may be given, in which case each qrunner will run once in
-round-robin fashion. The special runner `All' is shorthand for a qrunner for
-each listed by the -l option."""))
- self.parser.add_option(
- '-o', '--once',
- default=False, action='store_true', help=_("""\
-Run each named qrunner exactly once through its main loop. Otherwise, each
-qrunner runs indefinitely, until the process receives signal."""))
- self.parser.add_option(
- '-l', '--list',
- default=False, action='store_true',
- help=_('List the available qrunner names and exit.'))
- self.parser.add_option(
- '-v', '--verbose',
- default=0, action='count', help=_("""\
-Display more debugging information to the logs/qrunner log file."""))
- self.parser.add_option(
- '-s', '--subproc',
- default=False, action='store_true', help=_("""\
-This should only be used when running qrunner as a subprocess of the
-mailmanctl startup script. It changes some of the exit-on-error behavior to
-work better with that framework."""))
-
- def sanity_check(self):
- if self.arguments:
- self.parser.error(_('Unexpected arguments'))
- if not self.options.runners and not self.options.list:
- self.parser.error(_('No runner name given.'))
-
-
-
-def make_qrunner(name, slice, range, once=False):
- # Several conventions for specifying the runner name are supported. It
- # could be one of the shortcut names. If the name is a full module path,
- # use it explicitly. If the name starts with a dot, it's a class name
- # relative to the Mailman.queue package.
- qrunner_config = getattr(config, 'qrunner.' + name, None)
- if qrunner_config is not None:
- # It was a shortcut name.
- class_path = qrunner_config['class']
- elif name.startswith('.'):
- class_path = 'mailman.queue' + name
- else:
- class_path = name
- module_name, class_name = class_path.rsplit('.', 1)
- try:
- __import__(module_name)
- except ImportError, e:
- if config.options.options.subproc:
- # Exit with SIGTERM exit code so the master watcher won't try to
- # restart us.
- print >> sys.stderr, _('Cannot import runner module: $module_name')
- print >> sys.stderr, e
- sys.exit(signal.SIGTERM)
- else:
- raise
- qrclass = getattr(sys.modules[module_name], class_name)
- if once:
- # Subclass to hack in the setting of the stop flag in _do_periodic()
- class Once(qrclass):
- def _do_periodic(self):
- self.stop()
- qrunner = Once(name, slice)
- else:
- qrunner = qrclass(name, slice)
- return qrunner
-
-
-
-def set_signals(loop):
- """Set up the signal handlers.
-
- Signals caught are: SIGTERM, SIGINT, SIGUSR1 and SIGHUP. The latter is
- used to re-open the log files. SIGTERM and SIGINT are treated exactly the
- same -- they cause qrunner to exit with no restart from the master.
- SIGUSR1 also causes qrunner to exit, but the master watcher will restart
- it in that case.
-
- :param loop: A loop queue runner instance.
- """
- def sigterm_handler(signum, frame):
- # Exit the qrunner cleanly
- loop.stop()
- loop.status = signal.SIGTERM
- log.info('%s qrunner caught SIGTERM. Stopping.', loop.name())
- signal.signal(signal.SIGTERM, sigterm_handler)
- def sigint_handler(signum, frame):
- # Exit the qrunner cleanly
- loop.stop()
- loop.status = signal.SIGINT
- log.info('%s qrunner caught SIGINT. Stopping.', loop.name())
- signal.signal(signal.SIGINT, sigint_handler)
- def sigusr1_handler(signum, frame):
- # Exit the qrunner cleanly
- loop.stop()
- loop.status = signal.SIGUSR1
- log.info('%s qrunner caught SIGUSR1. Stopping.', loop.name())
- signal.signal(signal.SIGUSR1, sigusr1_handler)
- # SIGHUP just tells us to rotate our log files.
- def sighup_handler(signum, frame):
- reopen()
- log.info('%s qrunner caught SIGHUP. Reopening logs.', loop.name())
- signal.signal(signal.SIGHUP, sighup_handler)
-
-
-
-def main():
- global log
-
- options = ScriptOptions()
- options.initialize()
-
- if options.options.list:
- prefixlen = max(len(shortname)
- for shortname in config.qrunner_shortcuts)
- for shortname in sorted(config.qrunner_shortcuts):
- runnername = config.qrunner_shortcuts[shortname]
- shortname = (' ' * (prefixlen - len(shortname))) + shortname
- print _('$shortname runs $runnername')
- sys.exit(0)
-
- # Fast track for one infinite runner
- if len(options.options.runners) == 1 and not options.options.once:
- qrunner = make_qrunner(*options.options.runners[0])
- class Loop:
- status = 0
- def __init__(self, qrunner):
- self._qrunner = qrunner
- def name(self):
- return self._qrunner.__class__.__name__
- def stop(self):
- self._qrunner.stop()
- loop = Loop(qrunner)
- set_signals(loop)
- # Now start up the main loop
- log = logging.getLogger('mailman.qrunner')
- log.info('%s qrunner started.', loop.name())
- qrunner.run()
- log.info('%s qrunner exiting.', loop.name())
- else:
- # Anything else we have to handle a bit more specially
- qrunners = []
- for runner, rslice, rrange in options.options.runners:
- qrunner = make_qrunner(runner, rslice, rrange, once=True)
- qrunners.append(qrunner)
- # This class is used to manage the main loop
- class Loop:
- status = 0
- def __init__(self):
- self._isdone = False
- def name(self):
- return 'Main loop'
- def stop(self):
- self._isdone = True
- def isdone(self):
- return self._isdone
- loop = Loop()
- set_signals(loop)
- log.info('Main qrunner loop started.')
- while not loop.isdone():
- for qrunner in qrunners:
- # In case the SIGTERM came in the middle of this iteration
- if loop.isdone():
- break
- if options.options.verbose:
- log.info('Now doing a %s qrunner iteration',
- qrunner.__class__.__bases__[0].__name__)
- qrunner.run()
- if options.options.once:
- break
- log.info('Main qrunner loop exiting.')
- # All done
- sys.exit(loop.status)
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/remove_list.py b/mailman/bin/remove_list.py
deleted file mode 100644
index 05211b200..000000000
--- a/mailman/bin/remove_list.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# Copyright (C) 1998-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/>.
-
-import sys
-
-from mailman.app.lifecycle import remove_list
-from mailman.config import config
-from mailman.i18n import _
-from mailman.options import MultipleMailingListOptions
-
-
-
-class ScriptOptions(MultipleMailingListOptions):
- usage = _("""\
-%prog [options]
-
-Remove the components of a mailing list with impunity - beware!
-
-This removes (almost) all traces of a mailing list. By default, the lists
-archives are not removed, which is very handy for retiring old lists.
-""")
-
- def add_options(self):
- super(ScriptOptions, self).add_options()
- self.parser.add_option(
- '-a', '--archives',
- default=False, action='store_true',
- help=_("""\
-Remove the list's archives too, or if the list has already been deleted,
-remove any residual archives."""))
- self.parser.add_option(
- '-q', '--quiet',
- default=False, action='store_true',
- help=_('Suppress status messages'))
-
- def sanity_check(self):
- if len(self.options.listnames) == 0:
- self.parser.error(_('Nothing to do'))
- if len(self.arguments) > 0:
- self.parser.error(_('Unexpected arguments'))
-
-
-
-def main():
- options = ScriptOptions()
- options.initialize()
-
- for fqdn_listname in options.options.listnames:
- if not options.options.quiet:
- print _('Removing list: $fqdn_listname')
- mlist = config.db.list_manager.get(fqdn_listname)
- if mlist is None:
- if options.options.archives:
- print _("""\
-No such list: ${fqdn_listname}. Removing its residual archives.""")
- else:
- print >> sys.stderr, _(
- 'No such list (or list already deleted): $fqdn_listname')
-
- if not options.options.archives:
- print _('Not removing archives. Reinvoke with -a to remove them.')
-
- remove_list(fqdn_listname, mlist, options.options.archives)
- config.db.commit()
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/senddigests.py b/mailman/bin/senddigests.py
deleted file mode 100644
index fb057d6b9..000000000
--- a/mailman/bin/senddigests.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# Copyright (C) 1998-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/>.
-
-import os
-import sys
-import optparse
-
-from mailman import MailList
-from mailman.i18n import _
-from mailman.initialize import initialize
-from mailman.version import MAILMAN_VERSION
-
-# Work around known problems with some RedHat cron daemons
-import signal
-signal.signal(signal.SIGCHLD, signal.SIG_DFL)
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-%prog [options]
-
-Dispatch digests for lists w/pending messages and digest_send_periodic
-set."""))
- parser.add_option('-l', '--listname',
- type='string', default=[], action='append',
- dest='listnames', help=_("""\
-Send the digest for the given list only, otherwise the digests for all
-lists are sent out. Multiple -l options may be given."""))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- if args:
- parser.print_help()
- print >> sys.stderr, _('Unexpected arguments')
- sys.exit(1)
- return opts, args, parser
-
-
-
-def main():
- opts, args, parser = parseargs()
- initialize(opts.config)
-
- for listname in set(opts.listnames or config.list_manager.names):
- mlist = MailList.MailList(listname, lock=False)
- if mlist.digest_send_periodic:
- mlist.Lock()
- try:
- try:
- mlist.send_digest_now()
- mlist.Save()
- # We are unable to predict what exception may occur in digest
- # processing and we don't want to lose the other digests, so
- # we catch everything.
- except Exception, errmsg:
- print >> sys.stderr, \
- 'List: %s: problem processing %s:\n%s' % \
- (listname,
- os.path.join(mlist.data_path, 'digest.mbox'),
- errmsg)
- finally:
- mlist.Unlock()
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/set_members.py b/mailman/bin/set_members.py
deleted file mode 100644
index cdd11c56f..000000000
--- a/mailman/bin/set_members.py
+++ /dev/null
@@ -1,189 +0,0 @@
-# Copyright (C) 2007-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/>.
-
-import csv
-import optparse
-
-from mailman import Message
-from mailman import Utils
-from mailman import i18n
-from mailman import passwords
-from mailman.app.membership import add_member
-from mailman.app.notifications import (
- send_admin_subscription_notice, send_welcome_message)
-from mailman.configuration import config
-from mailman.initialize import initialize
-from mailman.interfaces import DeliveryMode
-from mailman.version import MAILMAN_VERSION
-
-
-_ = i18n._
-
-DELIVERY_MODES = {
- 'regular': DeliveryMode.regular,
- 'plain': DeliveryMode.plaintext_digests,
- 'mime': DeliveryMode.mime_digests,
- }
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-%prog [options] csv-file
-
-Set the membership of a mailing list to that described in a CSV file. Each
-row of the CSV file has the following format. Only the address column is
-required.
-
- - email address
- - full name (default: the empty string)
- - delivery mode (default: regular delivery) [1]
-
-[1] The delivery mode is a case insensitive string of the following values:
-
- regular - regular, i.e. immediate delivery
- mime - MIME digest delivery
- plain - plain text (RFC 1153) digest delivery
-
-Any address not included in the CSV file is removed from the list membership.
-"""))
- parser.add_option('-l', '--listname',
- type='string', help=_("""\
-Mailng list to set the membership for."""))
- parser.add_option('-w', '--welcome-msg',
- type='string', metavar='<y|n>', help=_("""\
-Set whether or not to send the list members a welcome message, overriding
-whatever the list's 'send_welcome_msg' setting is."""))
- parser.add_option('-a', '--admin-notify',
- type='string', metavar='<y|n>', help=_("""\
-Set whether or not to send the list administrators a notification on the
-success/failure of these subscriptions, overriding whatever the list's
-'admin_notify_mchanges' setting is."""))
- parser.add_option('-v', '--verbose', action='store_true',
- help=_('Increase verbosity'))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- if opts.welcome_msg is not None:
- ch = opts.welcome_msg[0].lower()
- if ch == 'y':
- opts.welcome_msg = True
- elif ch == 'n':
- opts.welcome_msg = False
- else:
- parser.error(_('Illegal value for -w: $opts.welcome_msg'))
- if opts.admin_notify is not None:
- ch = opts.admin_notify[0].lower()
- if ch == 'y':
- opts.admin_notify = True
- elif ch == 'n':
- opts.admin_notify = False
- else:
- parser.error(_('Illegal value for -a: $opts.admin_notify'))
- return parser, opts, args
-
-
-
-def parse_file(filename):
- members = {}
- with open(filename) as fp:
- for row in csv.reader(fp):
- if len(row) == 0:
- continue
- elif len(row) == 1:
- address = row[0]
- real_name = None
- delivery_mode = DeliveryMode.regular
- elif len(row) == 2:
- address, real_name = row
- delivery_mode = DeliveryMode.regular
- else:
- # Ignore extra columns
- address, real_name = row[0:2]
- delivery_mode = DELIVERY_MODES.get(row[2].lower())
- if delivery_mode is None:
- delivery_mode = DeliveryMode.regular
- members[address] = real_name, delivery_mode
- return members
-
-
-
-def main():
- parser, opts, args = parseargs()
- initialize(opts.config)
-
- mlist = config.db.list_manager.get(opts.listname)
- if mlist is None:
- parser.error(_('No such list: $opts.listname'))
-
- # Set up defaults.
- if opts.welcome_msg is None:
- send_welcome_msg = mlist.send_welcome_msg
- else:
- send_welcome_msg = opts.welcome_msg
- if opts.admin_notify is None:
- admin_notify = mlist.admin_notify_mchanges
- else:
- admin_notify = opts.admin_notify
-
- # Parse the csv files.
- member_data = {}
- for filename in args:
- member_data.update(parse_file(filename))
-
- future_members = set(member_data)
- current_members = set(obj.address for obj in mlist.members.addresses)
- add_members = future_members - current_members
- delete_members = current_members - future_members
- change_members = current_members & future_members
-
- with i18n.using_language(mlist.preferred_language):
- # Start by removing all the delete members.
- for address in delete_members:
- print _('deleting address: $address')
- member = mlist.members.get_member(address)
- member.unsubscribe()
- # For all members that are in both lists, update their full name and
- # delivery mode.
- for address in change_members:
- print _('updating address: $address')
- real_name, delivery_mode = member_data[address]
- member = mlist.members.get_member(address)
- member.preferences.delivery_mode = delivery_mode
- user = config.db.user_manager.get_user(address)
- user.real_name = real_name
- for address in add_members:
- print _('adding address: $address')
- real_name, delivery_mode = member_data[address]
- password = passwords.make_secret(
- Utils.MakeRandomPassword(),
- passwords.lookup_scheme(config.PASSWORD_SCHEME))
- add_member(mlist, address, real_name, password, delivery_mode,
- mlist.preferred_language, send_welcome_msg,
- admin_notify)
- if send_welcome_msg:
- send_welcome_message(mlist, address, language, delivery_mode)
- if admin_notify:
- send_admin_subscription_notice(mlist, address, real_name)
-
- config.db.flush()
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/show_config.py b/mailman/bin/show_config.py
deleted file mode 100644
index 8d26c5c97..000000000
--- a/mailman/bin/show_config.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# Copyright (C) 2006-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/>.
-
-import re
-import sys
-import pprint
-import optparse
-
-from mailman.configuration import config
-from mailman.i18n import _
-from mailman.version import MAILMAN_VERSION
-
-
-# List of names never to show even if --verbose
-NEVER_SHOW = ['__builtins__', '__doc__']
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-%%prog [options] [pattern ...]
-
-Show the values of various Defaults.py/mailman.cfg variables.
-If one or more patterns are given, show only those variables
-whose names match a pattern"""))
- parser.add_option('-v', '--verbose',
- default=False, action='store_true',
- help=_(
-"Show all configuration names, not just 'settings'."))
- parser.add_option('-i', '--ignorecase',
- default=False, action='store_true',
- help=_("Match patterns case-insensitively."))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- return parser, opts, args
-
-
-
-def main():
- parser, opts, args = parseargs()
-
- patterns = []
- if opts.ignorecase:
- flag = re.IGNORECASE
- else:
- flag = 0
- for pattern in args:
- patterns.append(re.compile(pattern, flag))
-
- pp = pprint.PrettyPrinter(indent=4)
- config.load(opts.config)
- names = config.__dict__.keys()
- names.sort()
- for name in names:
- if name in NEVER_SHOW:
- continue
- if not opts.verbose:
- if name.startswith('_') or re.search('[a-z]', name):
- continue
- if patterns:
- hit = False
- for pattern in patterns:
- if pattern.search(name):
- hit = True
- break
- if not hit:
- continue
- value = config.__dict__[name]
- if isinstance(value, str):
- if re.search('\n', value):
- print '%s = """%s"""' %(name, value)
- else:
- print "%s = '%s'" % (name, value)
- else:
- print '%s = ' % name,
- pp.pprint(value)
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/show_qfiles.py b/mailman/bin/show_qfiles.py
deleted file mode 100644
index e4b64e0cd..000000000
--- a/mailman/bin/show_qfiles.py
+++ /dev/null
@@ -1,91 +0,0 @@
-# Copyright (C) 2006-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/>.
-
-import os
-import sys
-
-from cPickle import load
-
-from mailman.config import config
-from mailman.i18n import _
-from mailman.options import Options
-
-
-
-class ScriptOptions(Options):
- usage = _("""
-%%prog [options] qfiles ...
-
-Show the contents of one or more Mailman queue files.""")
-
- def add_options(self):
- super(ScriptOptions, self).add_options()
- self.parser.add_option(
- '-q', '--quiet',
- default=False, action='store_true',
- help=_("Don't print 'helpful' message delimiters."))
- self.parser.add_option(
- '-s', '--summary',
- default=False, action='store_true',
- help=_('Show a summary of queue files.'))
-
-
-
-def main():
- options = ScriptOptions()
- options.initialize()
-
- if options.options.summary:
- queue_totals = {}
- files_by_queue = {}
- for switchboard in config.switchboards.values():
- total = 0
- file_mappings = {}
- for filename in os.listdir(switchboard.queue_directory):
- base, ext = os.path.splitext(filename)
- file_mappings[ext] = file_mappings.get(ext, 0) + 1
- total += 1
- files_by_queue[switchboard.queue_directory] = file_mappings
- queue_totals[switchboard.queue_directory] = total
- # Sort by queue name.
- for queue_directory in sorted(files_by_queue):
- total = queue_totals[queue_directory]
- print queue_directory
- print _('\tfile count: $total')
- file_mappings = files_by_queue[queue_directory]
- for ext in sorted(file_mappings):
- print '\t{0}: {1}'.format(ext, file_mappings[ext])
- return
- # No summary.
- for filename in options.arguments:
- if not options.options.quiet:
- print '====================>', filename
- with open(filename) as fp:
- if filename.endswith('.pck'):
- msg = load(fp)
- data = load(fp)
- if data.get('_parsemsg'):
- sys.stdout.write(msg)
- else:
- sys.stdout.write(msg.as_string())
- else:
- sys.stdout.write(fp.read())
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/unshunt.py b/mailman/bin/unshunt.py
deleted file mode 100644
index fc889377c..000000000
--- a/mailman/bin/unshunt.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright (C) 2002-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/>.
-
-__metaclass__ = type
-__all__ = [
- 'main',
- ]
-
-
-import sys
-
-from mailman.config import config
-from mailman.i18n import _
-from mailman.options import Options
-
-
-
-def main():
- options = Options()
- options.initialize()
-
- switchboard = config.switchboards['shunt']
- switchboard.recover_backup_files()
-
- for filebase in switchboard.files:
- try:
- msg, msgdata = switchboard.dequeue(filebase)
- whichq = msgdata.get('whichq', 'in')
- config.switchboards[whichq].enqueue(msg, msgdata)
- except Exception, e:
- # If there are any unshunting errors, log them and continue trying
- # other shunted messages.
- print >> sys.stderr, _(
- 'Cannot unshunt message $filebase, skipping:\n$e')
- else:
- # Unlink the .bak file left by dequeue()
- switchboard.finish(filebase)
diff --git a/mailman/bin/update.py b/mailman/bin/update.py
deleted file mode 100644
index 34ea6cda3..000000000
--- a/mailman/bin/update.py
+++ /dev/null
@@ -1,660 +0,0 @@
-# Copyright (C) 1998-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/>.
-
-import os
-import md5
-import sys
-import time
-import email
-import errno
-import shutil
-import cPickle
-import marshal
-import optparse
-
-from locknix.lockfile import TimeOutError
-
-from mailman import MailList
-from mailman import Message
-from mailman import Pending
-from mailman import Utils
-from mailman import version
-from mailman.MemberAdaptor import BYBOUNCE, ENABLED
-from mailman.OldStyleMemberships import OldStyleMemberships
-from mailman.Queue.Switchboard import Switchboard
-from mailman.configuration import config
-from mailman.i18n import _
-from mailman.initialize import initialize
-from mailman.utilities.filesystem import makedirs
-
-
-FRESH = 0
-NOTFRESH = -1
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=version.MAILMAN_VERSION,
- usage=_("""\
-Perform all necessary upgrades.
-
-%prog [options]"""))
- parser.add_option('-f', '--force',
- default=False, action='store_true', help=_("""\
-Force running the upgrade procedures. Normally, if the version number of the
-installed Mailman matches the current version number (or a 'downgrade' is
-detected), nothing will be done."""))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- if args:
- parser.print_help()
- print >> sys.stderr, _('Unexpected arguments')
- sys.exit(1)
- return parser, opts, args
-
-
-
-def calcversions():
- # Returns a tuple of (lastversion, thisversion). If the last version
- # could not be determined, lastversion will be FRESH or NOTFRESH,
- # depending on whether this installation appears to be fresh or not. The
- # determining factor is whether there are files in the $var_prefix/logs
- # subdir or not. The version numbers are HEX_VERSIONs.
- #
- # See if we stored the last updated version
- lastversion = None
- thisversion = version.HEX_VERSION
- try:
- fp = open(os.path.join(config.DATA_DIR, 'last_mailman_version'))
- data = fp.read()
- fp.close()
- lastversion = int(data, 16)
- except (IOError, ValueError):
- pass
- #
- # try to figure out if this is a fresh install
- if lastversion is None:
- lastversion = FRESH
- try:
- if os.listdir(config.LOG_DIR):
- lastversion = NOTFRESH
- except OSError:
- pass
- return (lastversion, thisversion)
-
-
-
-def makeabs(relpath):
- return os.path.join(config.PREFIX, relpath)
-
-
-def make_varabs(relpath):
- return os.path.join(config.VAR_PREFIX, relpath)
-
-
-
-def move_language_templates(mlist):
- listname = mlist.internal_name()
- print _('Fixing language templates: $listname')
- # Mailman 2.1 has a new cascading search for its templates, defined and
- # described in Utils.py:maketext(). Putting templates in the top level
- # templates/ subdir or the lists/<listname> subdir is deprecated and no
- # longer searched..
- #
- # What this means is that most templates can live in the global templates/
- # subdirectory, and only needs to be copied into the list-, vhost-, or
- # site-specific language directories when needed.
- #
- # Also, by default all standard (i.e. English) templates must now live in
- # the templates/en directory. This update cleans up all the templates,
- # deleting more-specific duplicates (as calculated by md5 checksums) in
- # favor of more-global locations.
- #
- # First, get rid of any lists/<list> template or lists/<list>/en template
- # that is identical to the global templates/* default.
- for gtemplate in os.listdir(os.path.join(config.TEMPLATE_DIR, 'en')):
- # BAW: get rid of old templates, e.g. admlogin.txt and
- # handle_opts.html
- try:
- fp = open(os.path.join(config.TEMPLATE_DIR, gtemplate))
- except IOError, e:
- if e.errno <> errno.ENOENT:
- raise
- # No global template
- continue
- gcksum = md5.new(fp.read()).digest()
- fp.close()
- # Match against the lists/<list>/* template
- try:
- fp = open(os.path.join(mlist.fullpath(), gtemplate))
- except IOError, e:
- if e.errno <> errno.ENOENT:
- raise
- else:
- tcksum = md5.new(fp.read()).digest()
- fp.close()
- if gcksum == tcksum:
- os.unlink(os.path.join(mlist.fullpath(), gtemplate))
- # Match against the lists/<list>/*.prev template
- try:
- fp = open(os.path.join(mlist.fullpath(), gtemplate + '.prev'))
- except IOError, e:
- if e.errno <> errno.ENOENT:
- raise
- else:
- tcksum = md5.new(fp.read()).digest()
- fp.close()
- if gcksum == tcksum:
- os.unlink(os.path.join(mlist.fullpath(), gtemplate + '.prev'))
- # Match against the lists/<list>/en/* templates
- try:
- fp = open(os.path.join(mlist.fullpath(), 'en', gtemplate))
- except IOError, e:
- if e.errno <> errno.ENOENT:
- raise
- else:
- tcksum = md5.new(fp.read()).digest()
- fp.close()
- if gcksum == tcksum:
- os.unlink(os.path.join(mlist.fullpath(), 'en', gtemplate))
- # Match against the templates/* template
- try:
- fp = open(os.path.join(config.TEMPLATE_DIR, gtemplate))
- except IOError, e:
- if e.errno <> errno.ENOENT:
- raise
- else:
- tcksum = md5.new(fp.read()).digest()
- fp.close()
- if gcksum == tcksum:
- os.unlink(os.path.join(config.TEMPLATE_DIR, gtemplate))
- # Match against the templates/*.prev template
- try:
- fp = open(os.path.join(config.TEMPLATE_DIR, gtemplate + '.prev'))
- except IOError, e:
- if e.errno <> errno.ENOENT:
- raise
- else:
- tcksum = md5.new(fp.read()).digest()
- fp.close()
- if gcksum == tcksum:
- os.unlink(os.path.join(config.TEMPLATE_DIR,
- gtemplate + '.prev'))
-
-
-
-def situate_list(listname):
- # This turns the directory called 'listname' into a directory called
- # 'listname@domain'. Start by finding out what the domain should be.
- # A list's domain is its email host.
- mlist = MailList.MailList(listname, lock=False, check_version=False)
- fullname = mlist.fqdn_listname
- oldpath = os.path.join(config.VAR_PREFIX, 'lists', listname)
- newpath = os.path.join(config.VAR_PREFIX, 'lists', fullname)
- if os.path.exists(newpath):
- print >> sys.stderr, _('WARNING: could not situate list: $listname')
- else:
- os.rename(oldpath, newpath)
- print _('situated list $listname to $fullname')
- return fullname
-
-
-
-def dolist(listname):
- mlist = MailList.MailList(listname, lock=False)
- try:
- mlist.Lock(0.5)
- except TimeOutError:
- print >> sys.stderr, _(
- 'WARNING: could not acquire lock for list: $listname')
- return 1
- # Sanity check the invariant that every BYBOUNCE disabled member must have
- # bounce information. Some earlier betas broke this. BAW: we're
- # submerging below the MemberAdaptor interface, so skip this if we're not
- # using OldStyleMemberships.
- if isinstance(mlist._memberadaptor, OldStyleMemberships):
- noinfo = {}
- for addr, (reason, when) in mlist.delivery_status.items():
- if reason == BYBOUNCE and not mlist.bounce_info.has_key(addr):
- noinfo[addr] = reason, when
- # What to do about these folks with a BYBOUNCE delivery status and no
- # bounce info? This number should be very small, and I think it's
- # fine to simple re-enable them and let the bounce machinery
- # re-disable them if necessary.
- n = len(noinfo)
- if n > 0:
- print _(
- 'Resetting $n BYBOUNCEs disabled addrs with no bounce info')
- for addr in noinfo.keys():
- mlist.setDeliveryStatus(addr, ENABLED)
-
- mbox_dir = make_varabs('archives/private/%s.mbox' % (listname))
- mbox_file = make_varabs('archives/private/%s.mbox/%s' % (listname,
- listname))
- o_pub_mbox_file = make_varabs('archives/public/%s' % (listname))
- o_pri_mbox_file = make_varabs('archives/private/%s' % (listname))
- html_dir = o_pri_mbox_file
- 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):
- makedirs(mbox_dir)
- else:
- # This shouldn't happen, but hey, just in case
- if not os.path.isdir(mbox_dir):
- print _("""\
-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))
- 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):
- if mlist.archive_private:
- print _("""\
-
-$listname has both public and private mbox archives. Since this list
-currently uses private archiving, I'm installing the private mbox archive --
-$o_pri_mbox_file -- as the active archive, and renaming
- $o_pub_mbox_file
-to
- ${o_pub_mbox_file}.preb6
-
-You can integrate that into the archives if you want by using the 'arch'
-script.
-""") % (mlist._internal_name, o_pri_mbox_file, o_pub_mbox_file,
- o_pub_mbox_file)
- os.rename(o_pub_mbox_file, "%s.preb6" % (o_pub_mbox_file))
- else:
- print _("""\
-$mlist._internal_name has both public and private mbox archives. Since this
-list currently uses public archiving, I'm installing the public mbox file
-archive file ($o_pub_mbox_file) as the active one, and renaming
-$o_pri_mbox_file to ${o_pri_mbox_file}.preb6
-
-You can integrate that into the archives if you want by using the 'arch'
-script.
-""")
- os.rename(o_pri_mbox_file, "%s.preb6" % (o_pri_mbox_file))
- # Move private archive mbox there if it's around
- # and take into account all sorts of absurdities
- print _('- updating old private mbox file')
- if os.path.exists(o_pri_mbox_file):
- if os.path.isfile(o_pri_mbox_file):
- os.rename(o_pri_mbox_file, mbox_file)
- elif not os.path.isdir(o_pri_mbox_file):
- newname = "%s.mm_install-dunno_what_this_was_but_its_in_the_way" \
- % o_pri_mbox_file
- os.rename(o_pri_mbox_file, newname)
- print _("""\
- unknown file in the way, moving
- $o_pri_mbox_file
- to
- $newname""")
- else:
- # directory
- print _("""\
- looks like you have a really recent development installation...
- you're either one brave soul, or you already ran me""")
- # Move public archive mbox there if it's around
- # and take into account all sorts of absurdities.
- print _('- updating old public mbox file')
- if os.path.exists(o_pub_mbox_file):
- if os.path.isfile(o_pub_mbox_file):
- os.rename(o_pub_mbox_file, mbox_file)
- elif not os.path.isdir(o_pub_mbox_file):
- newname = "%s.mm_install-dunno_what_this_was_but_its_in_the_way" \
- % o_pub_mbox_file
- os.rename(o_pub_mbox_file, newname)
- print _("""\
- unknown file in the way, moving
- $o_pub_mbox_file
- to
- $newname""")
- else: # directory
- print _("""\
- looks like you have a really recent development installation...
- you're either one brave soul, or you already ran me""")
- # Move the html archives there
- if os.path.isdir(o_html_dir):
- os.rename(o_html_dir, html_dir)
- # chmod the html archives
- os.chmod(html_dir, 02775)
- # BAW: Is this still necessary?!
- mlist.Save()
- # Check to see if pre-b4 list-specific templates are around
- # and move them to the new place if there's not already
- # a new one there
- tmpl_dir = os.path.join(config.PREFIX, "templates")
- list_dir = os.path.join(config.PREFIX, "lists")
- b4_tmpl_dir = os.path.join(tmpl_dir, mlist._internal_name)
- new_tmpl_dir = os.path.join(list_dir, mlist._internal_name)
- if os.path.exists(b4_tmpl_dir):
- print _("""\
-- This list looks like it might have <= b4 list templates around""")
- for f in os.listdir(b4_tmpl_dir):
- o_tmpl = os.path.join(b4_tmpl_dir, f)
- n_tmpl = os.path.join(new_tmpl_dir, f)
- if os.path.exists(o_tmpl):
- if not os.path.exists(n_tmpl):
- os.rename(o_tmpl, n_tmpl)
- print _('- moved $o_tmpl to $n_tmpl')
- else:
- print _("""\
-- both $o_tmpl and $n_tmpl exist, leaving untouched""")
- else:
- print _("""\
-- $o_tmpl doesn't exist, leaving untouched""")
- # Move all the templates to the en language subdirectory as required for
- # Mailman 2.1
- move_language_templates(mlist)
- # Avoid eating filehandles with the list lockfiles
- mlist.Unlock()
- return 0
-
-
-
-def archive_path_fixer(unused_arg, dir, files):
- # Passed to os.path.walk to fix the perms on old html archives.
- for f in files:
- abs = os.path.join(dir, f)
- if os.path.isdir(abs):
- if f == "database":
- os.chmod(abs, 02770)
- else:
- os.chmod(abs, 02775)
- elif os.path.isfile(abs):
- os.chmod(abs, 0664)
-
-
-def remove_old_sources(module):
- # Also removes old directories.
- src = '%s/%s' % (config.PREFIX, module)
- pyc = src + "c"
- if os.path.isdir(src):
- print _('removing directory $src and everything underneath')
- shutil.rmtree(src)
- elif os.path.exists(src):
- print _('removing $src')
- try:
- os.unlink(src)
- except os.error, rest:
- print _("Warning: couldn't remove $src -- $rest")
- if module.endswith('.py') and os.path.exists(pyc):
- try:
- os.unlink(pyc)
- except OSError, rest:
- print _("couldn't remove old file $pyc -- $rest")
-
-
-
-def update_qfiles():
- print _('updating old qfiles')
- prefix = `time.time()` + '+'
- # Be sure the qfiles/in directory exists (we don't really need the
- # switchboard object, but it's convenient for creating the directory).
- sb = Switchboard(config.INQUEUE_DIR)
- for filename in os.listdir(config.QUEUE_DIR):
- # Updating means just moving the .db and .msg files to qfiles/in where
- # it should be dequeued, converted, and processed normally.
- if os.path.splitext(filename) == '.msg':
- oldmsgfile = os.path.join(config.QUEUE_DIR, filename)
- newmsgfile = os.path.join(config.INQUEUE_DIR, prefix + filename)
- os.rename(oldmsgfile, newmsgfile)
- elif os.path.splitext(filename) == '.db':
- olddbfile = os.path.join(config.QUEUE_DIR, filename)
- newdbfile = os.path.join(config.INQUEUE_DIR, prefix + filename)
- os.rename(olddbfile, newdbfile)
- # Now update for the Mailman 2.1.5 qfile format. For every filebase in
- # the qfiles/* directories that has both a .pck and a .db file, pull the
- # data out and re-queue them.
- for dirname in os.listdir(config.QUEUE_DIR):
- dirpath = os.path.join(config.QUEUE_DIR, dirname)
- if dirpath == config.BADQUEUE_DIR:
- # The files in qfiles/bad can't possibly be pickles
- continue
- sb = Switchboard(dirpath)
- try:
- for filename in os.listdir(dirpath):
- filepath = os.path.join(dirpath, filename)
- filebase, ext = os.path.splitext(filepath)
- # Handle the .db metadata files as part of the handling of the
- # .pck or .msg message files.
- if ext not in ('.pck', '.msg'):
- continue
- msg, data = dequeue(filebase)
- if msg is not None and data is not None:
- sb.enqueue(msg, data)
- except EnvironmentError, e:
- if e.errno <> errno.ENOTDIR:
- raise
- print _('Warning! Not a directory: $dirpath')
-
-
-
-# Implementations taken from the pre-2.1.5 Switchboard
-def ext_read(filename):
- fp = open(filename)
- d = marshal.load(fp)
- # Update from version 2 files
- if d.get('version', 0) == 2:
- del d['filebase']
- # Do the reverse conversion (repr -> float)
- for attr in ['received_time']:
- try:
- sval = d[attr]
- except KeyError:
- pass
- else:
- # Do a safe eval by setting up a restricted execution
- # environment. This may not be strictly necessary since we
- # know they are floats, but it can't hurt.
- d[attr] = eval(sval, {'__builtins__': {}})
- fp.close()
- return d
-
-
-def dequeue(filebase):
- # Calculate the .db and .msg filenames from the given filebase.
- msgfile = os.path.join(filebase + '.msg')
- pckfile = os.path.join(filebase + '.pck')
- dbfile = os.path.join(filebase + '.db')
- # Now we are going to read the message and metadata for the given
- # filebase. We want to read things in this order: first, the metadata
- # file to find out whether the message is stored as a pickle or as
- # plain text. Second, the actual message file. However, we want to
- # first unlink the message file and then the .db file, because the
- # qrunner only cues off of the .db file
- msg = None
- try:
- data = ext_read(dbfile)
- os.unlink(dbfile)
- except EnvironmentError, e:
- if e.errno <> errno.ENOENT:
- raise
- data = {}
- # Between 2.1b4 and 2.1b5, the `rejection-notice' key in the metadata
- # was renamed to `rejection_notice', since dashes in the keys are not
- # supported in METAFMT_ASCII.
- if data.has_key('rejection-notice'):
- data['rejection_notice'] = data['rejection-notice']
- del data['rejection-notice']
- msgfp = None
- try:
- try:
- msgfp = open(pckfile)
- msg = cPickle.load(msgfp)
- os.unlink(pckfile)
- except EnvironmentError, e:
- if e.errno <> errno.ENOENT: raise
- msgfp = None
- try:
- msgfp = open(msgfile)
- msg = email.message_from_file(msgfp, Message.Message)
- os.unlink(msgfile)
- except EnvironmentError, e:
- if e.errno <> errno.ENOENT: raise
- except (email.Errors.MessageParseError, ValueError), e:
- # This message was unparsable, most likely because its
- # MIME encapsulation was broken. For now, there's not
- # much we can do about it.
- print _('message is unparsable: $filebase')
- msgfp.close()
- msgfp = None
- if config.QRUNNER_SAVE_BAD_MESSAGES:
- # Cheapo way to ensure the directory exists w/ the
- # proper permissions.
- sb = Switchboard(config.BADQUEUE_DIR)
- os.rename(msgfile, os.path.join(
- config.BADQUEUE_DIR, filebase + '.txt'))
- else:
- os.unlink(msgfile)
- msg = data = None
- except EOFError:
- # For some reason the pckfile was empty. Just delete it.
- print _('Warning! Deleting empty .pck file: $pckfile')
- os.unlink(pckfile)
- finally:
- if msgfp:
- msgfp.close()
- return msg, data
-
-
-
-def main():
- parser, opts, args = parseargs()
- initialize(opts.config)
-
- # calculate the versions
- lastversion, thisversion = calcversions()
- hexlversion = hex(lastversion)
- hextversion = hex(thisversion)
- if lastversion == thisversion and not opts.force:
- # nothing to do
- print _('No updates are necessary.')
- sys.exit(0)
- if lastversion > thisversion and not opts.force:
- print _("""\
-Downgrade detected, from version $hexlversion to version $hextversion
-This is probably not safe.
-Exiting.""")
- sys.exit(1)
- print _('Upgrading from version $hexlversion to $hextversion')
- errors = 0
- # get rid of old stuff
- print _('getting rid of old source files')
- for mod in ('mailman/Archiver.py', 'mailman/HyperArch.py',
- 'mailman/HyperDatabase.py', 'mailman/pipermail.py',
- 'mailman/smtplib.py', 'mailman/Cookie.py',
- 'bin/update_to_10b6', 'scripts/mailcmd',
- 'scripts/mailowner', 'mail/wrapper', 'mailman/pythonlib',
- 'cgi-bin/archives', 'mailman/MailCommandHandler'):
- remove_old_sources(mod)
- if not config.list_manager.names:
- print _('no lists == nothing to do, exiting')
- return
- # For people with web archiving, make sure the directories
- # in the archiving are set with proper perms for b6.
- if os.path.isdir("%s/public_html/archives" % config.PREFIX):
- print _("""\
-fixing all the perms on your old html archives to work with b6
-If your archives are big, this could take a minute or two...""")
- os.path.walk("%s/public_html/archives" % config.PREFIX,
- archive_path_fixer, "")
- print _('done')
- for listname in config.list_manager.names:
- # With 2.2.0a0, all list names grew an @domain suffix. If you find a
- # list without that, move it now.
- if not '@' in listname:
- listname = situate_list(listname)
- print _('Updating mailing list: $listname')
- errors += dolist(listname)
- print
- print _('Updating Usenet watermarks')
- wmfile = os.path.join(config.DATA_DIR, 'gate_watermarks')
- try:
- fp = open(wmfile)
- except IOError:
- print _('- nothing to update here')
- else:
- d = marshal.load(fp)
- fp.close()
- for listname in d.keys():
- if listname not in listnames:
- # this list no longer exists
- continue
- mlist = MailList.MailList(listname, lock=0)
- try:
- mlist.Lock(0.5)
- except TimeOutError:
- print >> sys.stderr, _(
- 'WARNING: could not acquire lock for list: $listname')
- errors = errors + 1
- else:
- # Pre 1.0b7 stored 0 in the gate_watermarks file to indicate
- # that no gating had been done yet. Without coercing this to
- # None, the list could now suddenly get flooded.
- mlist.usenet_watermark = d[listname] or None
- mlist.Save()
- mlist.Unlock()
- os.unlink(wmfile)
- print _('- usenet watermarks updated and gate_watermarks removed')
- # In Mailman 2.1, the qfiles directory has a different structure and a
- # different content. Also, in Mailman 2.1.5 we collapsed the message
- # files from separate .msg (pickled Message objects) and .db (marshalled
- # dictionaries) to a shared .pck file containing two pickles.
- update_qfiles()
- # This warning was necessary for the upgrade from 1.0b9 to 1.0b10.
- # There's no good way of figuring this out for releases prior to 2.0beta2
- # :(
- if lastversion == NOTFRESH:
- print _("""
-
-NOTE NOTE NOTE NOTE NOTE
-
- You are upgrading an existing Mailman installation, but I can't tell what
- version you were previously running.
-
- If you are upgrading from Mailman 1.0b9 or earlier you will need to
- manually update your mailing lists. For each mailing list you need to
- copy the file templates/options.html lists/<listname>/options.html.
-
- However, if you have edited this file via the Web interface, you will have
- to merge your changes into this file, otherwise you will lose your
- changes.
-
-NOTE NOTE NOTE NOTE NOTE
-
-""")
- if not errors:
- # Record the version we just upgraded to
- fp = open(os.path.join(config.DATA_DIR, 'last_mailman_version'), 'w')
- fp.write(hex(config.HEX_VERSION) + '\n')
- fp.close()
- else:
- lockdir = config.LOCK_DIR
- print _('''\
-
-ERROR:
-
-The locks for some lists could not be acquired. This means that either
-Mailman was still active when you upgraded, or there were stale locks in the
-$lockdir directory.
-
-You must put Mailman into a quiescent state and remove all stale locks, then
-re-run "make update" manually. See the INSTALL and UPGRADE files for details.
-''')
diff --git a/mailman/bin/version.py b/mailman/bin/version.py
deleted file mode 100644
index 0fb2c5a5b..000000000
--- a/mailman/bin/version.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright (C) 1998-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/>.
-
-import optparse
-
-from mailman import version
-from mailman.i18n import _
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=version.MAILMAN_VERSION,
- usage=_("""\
-%prog
-
-Print the Mailman version and exit."""))
- opts, args = parser.parse_args()
- if args:
- parser.error(_('Unexpected arguments'))
- return parser, opts, args
-
-
-
-def main():
- parser, opts, args = parseargs()
- # Yes, this is kind of silly
- print _('Using $version.MAILMAN_VERSION ($version.CODENAME)')
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/mailman/bin/withlist.py b/mailman/bin/withlist.py
deleted file mode 100644
index 8f2d8a2b5..000000000
--- a/mailman/bin/withlist.py
+++ /dev/null
@@ -1,220 +0,0 @@
-# Copyright (C) 1998-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/>.
-
-import os
-import sys
-import optparse
-
-from mailman import interact
-from mailman.config import config
-from mailman.core.initialize import initialize
-from mailman.i18n import _
-from mailman.version import MAILMAN_VERSION
-
-
-LAST_MLIST = None
-VERBOSE = True
-
-
-
-def do_list(listname, args, func):
- global LAST_MLIST
-
- if '@' not in listname:
- listname += '@' + config.DEFAULT_EMAIL_HOST
-
- # XXX FIXME Remove this when this script is converted to
- # MultipleMailingListOptions.
- listname = listname.decode(sys.getdefaultencoding())
- mlist = config.db.list_manager.get(listname)
- if mlist is None:
- print >> sys.stderr, _('Unknown list: $listname')
- else:
- if VERBOSE:
- print >> sys.stderr, _('Loaded list: $listname')
- LAST_MLIST = mlist
- # Try to import the module and run the callable.
- if func:
- return func(mlist, *args)
- return None
-
-
-
-def parseargs():
- parser = optparse.OptionParser(version=MAILMAN_VERSION,
- usage=_("""\
-%prog [options] listname [args ...]
-
-General framework for interacting with a mailing list object.
-
-There are two ways to use this script: interactively or programmatically.
-Using it interactively allows you to play with, examine and modify a
-IMailinglist object from Python's interactive interpreter. When running
-interactively, a IMailingList object called 'm' will be available in the
-global namespace.
-
-Programmatically, you can write a function to operate on a IMailingList
-object, and this script will take care of the housekeeping (see below for
-examples). In that case, the general usage syntax is:
-
- % bin/withlist [options] listname [args ...]
-
-Here's an example of how to use the -r option. Say you have a file in the
-Mailman installation directory called 'listaddr.py', with the following
-two functions:
-
- def listaddr(mlist):
- print mlist.posting_address
-
- def requestaddr(mlist):
- print mlist.request_address
-
-Now, from the command line you can print the list's posting address by running
-the following from the command line:
-
- % bin/withlist -r listaddr mylist
- Loading list: mylist
- Importing listaddr ...
- Running listaddr.listaddr() ...
- mylist@myhost.com
-
-And you can print the list's request address by running:
-
- % bin/withlist -r listaddr.requestaddr mylist
- Loading list: mylist
- Importing listaddr ...
- Running listaddr.requestaddr() ...
- mylist-request@myhost.com
-
-As another example, say you wanted to change the password for a particular
-user on a particular list. You could put the following function in a file
-called 'changepw.py':
-
- from mailman.errors import NotAMemberError
-
- def changepw(mlist, addr, newpasswd):
- try:
- mlist.setMemberPassword(addr, newpasswd)
- mlist.Save()
- except NotAMemberError:
- print 'No address matched:', addr
-
-and run this from the command line:
-
- % bin/withlist -l -r changepw mylist somebody@somewhere.org foobar"""))
- parser.add_option('-i', '--interactive',
- default=None, action='store_true', help=_("""\
-Leaves you at an interactive prompt after all other processing is complete.
-This is the default unless the -r option is given."""))
- parser.add_option('-r', '--run',
- type='string', help=_("""\
-This can be used to run a script with the opened IMailingList object. This
-works by attempting to import'module' (which must be in the directory
-containing withlist, or already be accessible on your sys.path), and then
-calling 'callable' from the module. callable can be a class or function; it
-is called with the IMailingList object as the first argument. If additional
-args are given on the command line, they are passed as subsequent positional
-args to the callable.
-
-Note that 'module.' is optional; if it is omitted then a module with the name
-'callable' will be imported.
-
-The global variable 'r' will be set to the results of this call."""))
- parser.add_option('-a', '--all',
- default=False, action='store_true', help=_("""\
-This option only works with the -r option. Use this if you want to execute
-the script on all mailing lists. When you use -a you should not include a
-listname argument on the command line. The variable 'r' will be a list of all
-the results."""))
- parser.add_option('-q', '--quiet',
- default=False, action='store_true',
- help=_('Suppress all status messages.'))
- parser.add_option('-C', '--config',
- help=_('Alternative configuration file to use'))
- opts, args = parser.parse_args()
- return parser, opts, args
-
-
-
-def main():
- global VERBOSE
-
- parser, opts, args = parseargs()
- config_file = (os.getenv('MAILMAN_CONFIG_FILE')
- if opts.config is None
- else opts.config)
- initialize(config_file, not opts.quiet)
-
- VERBOSE = not opts.quiet
- # The default for interact is true unless -r was given
- if opts.interactive is None:
- if not opts.run:
- opts.interactive = True
- else:
- opts.interactive = False
-
- dolist = True
- if len(args) < 1 and not opts.all:
- warning = _('No list name supplied.')
- if opts.interactive:
- # Let them keep going
- print >> sys.stderr, warning
- dolist = False
- else:
- parser.error(warning)
-
- if opts.all and not opts.run:
- parser.error(_('--all requires --run'))
-
- # Try to import the module for the callable
- func = None
- if opts.run:
- i = opts.run.rfind('.')
- if i < 0:
- module = opts.run
- callable = opts.run
- else:
- module = opts.run[:i]
- callable = opts.run[i+1:]
- if VERBOSE:
- print >> sys.stderr, _('Importing $module ...')
- __import__(module)
- mod = sys.modules[module]
- if VERBOSE:
- print >> sys.stderr, _('Running ${module}.${callable}() ...')
- func = getattr(mod, callable)
-
- r = None
- if opts.all:
- r = [do_list(listname, args, func)
- for listname in config.list_manager.names]
- elif dolist:
- listname = args.pop(0).lower().strip()
- r = do_list(listname, args, func)
-
- # Now go to interactive mode, perhaps
- if opts.interactive:
- if dolist:
- banner = _(
- "The variable 'm' is the $listname mailing list")
- else:
- banner = interact.DEFAULT_BANNER
- overrides = dict(m=LAST_MLIST, r=r,
- commit=config.db.commit,
- abort=config.db.abort,
- config=config)
- interact.interact(upframe=False, banner=banner, overrides=overrides)