summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mailman/bin/bumpdigests.py74
-rw-r--r--src/mailman/bin/checkdbs.py209
-rw-r--r--src/mailman/bin/config_list.py332
-rw-r--r--src/mailman/bin/disabled.py201
-rw-r--r--src/mailman/bin/export.py310
-rw-r--r--src/mailman/bin/find_member.py135
-rw-r--r--src/mailman/bin/gate_news.py244
-rw-r--r--src/mailman/bin/list_owners.py90
-rw-r--r--src/mailman/bin/senddigests.py83
-rw-r--r--src/mailman/bin/show_config.py97
10 files changed, 0 insertions, 1775 deletions
diff --git a/src/mailman/bin/bumpdigests.py b/src/mailman/bin/bumpdigests.py
deleted file mode 100644
index f30772ca8..000000000
--- a/src/mailman/bin/bumpdigests.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# Copyright (C) 1998-2015 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.core.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:
- parser.print_help()
- print(_('No such list: $listname'), file=sys.stderr)
- sys.exit(1)
- try:
- mlist.bump_digest_volume()
- finally:
- mlist.Save()
- mlist.Unlock()
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/src/mailman/bin/checkdbs.py b/src/mailman/bin/checkdbs.py
deleted file mode 100644
index 61aa6b6f1..000000000
--- a/src/mailman/bin/checkdbs.py
+++ /dev/null
@@ -1,209 +0,0 @@
-# Copyright (C) 1998-2015 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 Utils
-from mailman.app.requests import handle_request
-from mailman.configuration import config
-from mailman.core.i18n import _
-from mailman.email.message import UserNotification
-from mailman.initialize import initialize
-from mailman.interfaces.requests import IListRequests, RequestType
-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 = 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(_('Unexpected arguments'), file=sys.stderr)
- sys.exit(1)
- return opts, args, parser
-
-
-
-def pending_requests(mlist):
- # Must return a byte string
- lcset = mlist.preferred_language.charset
- pending = []
- first = True
- requestsdb = IListRequests(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 = mlist.preferred_language.charset
- 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(mlist.preferred_language.charset)
- 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 = IListRequests(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
-
-
-
-# Figure out epoch seconds of midnight at the start of today (or the given
-# 3-tuple date of (year, month, day).
-def midnight(date=None):
- if date is None:
- date = time.localtime()[:3]
- # -1 for dst flag tells the library to figure it out
- return time.mktime(date + (0,)*5 + (-1,))
-
-
-def main():
- opts, args, parser = parseargs()
- initialize(opts.config)
-
- 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 = IListRequests(mlist).count
- # While we're at it, let's evict yesterday's autoresponse data
- midnight_today = midnight()
- evictions = []
- for sender in mlist.hold_and_cmd_autoresponses.keys():
- date, respcount = mlist.hold_and_cmd_autoresponses[sender]
- if 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:
- # Set the default language the the list's preferred language.
- _.default = 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,
- 'mail_host': mlist.mail_host,
- '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 = 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/src/mailman/bin/config_list.py b/src/mailman/bin/config_list.py
deleted file mode 100644
index a0b2a54f4..000000000
--- a/src/mailman/bin/config_list.py
+++ /dev/null
@@ -1,332 +0,0 @@
-# Copyright (C) 1998-2015 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 MailList
-from mailman import errors
-from mailman.configuration import config
-from mailman.core.i18n import _
-from mailman.initialize import initialize
-from mailman.utilities.string import wrap
-from mailman.version import MAILMAN_VERSION
-
-
-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.
- charset = mlist.preferred_language.charset
- # Set the system's default language.
- _.default = mlist.preferred_language.code
- 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 = mlist.preferred_language.charset
- 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 = 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 = 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 as error:
- parser.error(_('No such list "$listname"\n$error'))
- 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()
- initialize(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/src/mailman/bin/disabled.py b/src/mailman/bin/disabled.py
deleted file mode 100644
index b190556c2..000000000
--- a/src/mailman/bin/disabled.py
+++ /dev/null
@@ -1,201 +0,0 @@
-# Copyright (C) 2001-2015 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 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.core.i18n import _
-from mailman.interfaces.member import NotAMemberError
-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)
-x5o
-
-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 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(
- 'Cannot send disable notice to non-member: %s',
- member)
- mlist.ApprovedDeleteMember(member, 'cron/disabled')
- mlist.Save()
- finally:
- mlist.Unlock()
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/src/mailman/bin/export.py b/src/mailman/bin/export.py
deleted file mode 100644
index 279abc36f..000000000
--- a/src/mailman/bin/export.py
+++ /dev/null
@@ -1,310 +0,0 @@
-# Copyright (C) 2006-2015 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.core.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(_value.decode('utf-8'))
- 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/src/mailman/bin/find_member.py b/src/mailman/bin/find_member.py
deleted file mode 100644
index 349af8247..000000000
--- a/src/mailman/bin/find_member.py
+++ /dev/null
@@ -1,135 +0,0 @@
-# Copyright (C) 1998-2015 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.core.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/src/mailman/bin/gate_news.py b/src/mailman/bin/gate_news.py
deleted file mode 100644
index 72568cd1b..000000000
--- a/src/mailman/bin/gate_news.py
+++ /dev/null
@@ -1,244 +0,0 @@
-# Copyright (C) 1998-2015 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 flufl.lock import Lock, TimeOutError
-from lazr.config import as_host_port
-
-from mailman import MailList
-from mailman import Message
-from mailman import loginit
-from mailman.configuration import config
-from mailman.core.i18n import _
-from mailman.core.switchboard 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 = as_host_port(mlist.nntp_host, default_port=119)
- # 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) as 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
- # FIXME 2010-02-16 barry use List-Post header.
- 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 as 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,
- listid=mlist.list_id,
- fromusenet=True)
- log.info('posted to list %s: %7d', listname, num)
- except nntplib.NNTPError as 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 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 Lock(GATENEWS_LOCK_FILE,
- # It's okay to hijack this
- lifetime=LOCK_LIFETIME) as lock:
- process_lists(lock)
- clearcache()
- except TimeOutError:
- log.error('Could not acquire gate_news lock')
-
-
-
-if __name__ == '__main__':
- main()
diff --git a/src/mailman/bin/list_owners.py b/src/mailman/bin/list_owners.py
deleted file mode 100644
index 5b5fca2bf..000000000
--- a/src/mailman/bin/list_owners.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# Copyright (C) 2002-2015 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 zope.component import getUtility
-
-from mailman.MailList import MailList
-from mailman.core.i18n import _
-from mailman.initialize import initialize
-from mailman.interfaces.listmanager import IListManager
-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)
-
- list_manager = getUtility(IListManager)
- listnames = set(args or list_manager.names)
- bylist = {}
-
- for listname in listnames:
- mlist = list_manager.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/src/mailman/bin/senddigests.py b/src/mailman/bin/senddigests.py
deleted file mode 100644
index 59c03de2d..000000000
--- a/src/mailman/bin/senddigests.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# Copyright (C) 1998-2015 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.core.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 as 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/src/mailman/bin/show_config.py b/src/mailman/bin/show_config.py
deleted file mode 100644
index 290840ae3..000000000
--- a/src/mailman/bin/show_config.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# Copyright (C) 2006-2015 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.core.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()