aboutsummaryrefslogtreecommitdiffstats
path: root/bin
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xbin/add_members43
-rw-r--r--bin/list_lists14
-rwxr-xr-xbin/list_members38
-rw-r--r--bin/mailmanctl9
-rwxr-xr-xbin/newlist32
-rwxr-xr-xbin/rmlist2
-rwxr-xr-xbin/sync_members10
7 files changed, 97 insertions, 51 deletions
diff --git a/bin/add_members b/bin/add_members
index 77f11aff..a471919e 100755
--- a/bin/add_members
+++ b/bin/add_members
@@ -1,6 +1,6 @@
#! @PYTHON@
#
-# Copyright (C) 1998-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2013 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -52,6 +52,10 @@ Options:
the success/failure of these subscriptions, overriding whatever the
list's `admin_notify_mchanges' setting is.
+ --nomail
+ -n
+ Set the newly added members mail delivery to disabled by admin.
+
--help
-h
Print this help message and exit.
@@ -73,12 +77,13 @@ import paths
# Import this /after/ paths so that the sys.path is properly hacked
from email.Utils import parseaddr
-from Mailman import MailList
+from Mailman import i18n
from Mailman import Utils
-from Mailman import Message
-from Mailman import Errors
from Mailman import mm_cfg
-from Mailman import i18n
+from Mailman import Errors
+from Mailman import Message
+from Mailman import MailList
+from Mailman import MemberAdaptor
_ = i18n._
@@ -124,7 +129,7 @@ class UserDesc: pass
-def addall(mlist, members, digest, ack, outfp):
+def addall(mlist, members, digest, ack, outfp, nomail):
tee = Tee(outfp)
for member in members:
userdesc = UserDesc()
@@ -132,7 +137,11 @@ def addall(mlist, members, digest, ack, outfp):
userdesc.digest = digest
try:
- mlist.ApprovedAddMember(userdesc, ack, 0)
+ mlist.ApprovedAddMember(userdesc,
+ ack=ack,
+ admin_notif=False,
+ whence='bin/add_members',
+ )
except Errors.MMAlreadyAMember:
print >> tee, _('Already a member: %(member)s')
except Errors.MembershipIsBanned, pattern:
@@ -147,18 +156,20 @@ def addall(mlist, members, digest, ack, outfp):
print >> tee, _('Hostile address (illegal characters): %(member)s')
else:
print >> tee, _('Subscribed: %(member)s')
+ if nomail:
+ mlist.setDeliveryStatus(member, MemberAdaptor.BYADMIN)
def main():
try:
opts, args = getopt.getopt(sys.argv[1:],
- 'a:n:r:d:w:h',
+ 'a:r:d:w:nh',
['admin-notify=',
'regular-members-file=',
- 'non-digest-members-file=',
'digest-members-file=',
'welcome-msg=',
+ 'nomail',
'help'])
except getopt.error, msg:
usage(1, msg)
@@ -171,20 +182,14 @@ def main():
dfile = None
send_welcome_msg = None
admin_notif = None
+ nomail = False
for opt, arg in opts:
if opt in ('-h', '--help'):
usage(0)
elif opt in ('-d', '--digest-members-file'):
dfile = arg
- # Deprecate -/--non-digest-members-file or consistency with
- # list_members
elif opt in ('-r', '--regular-members-file'):
nfile = arg
- elif opt in ('-n', '--non-digest-members-file'):
- nfile = arg
- # I don't think we need to use the warnings module here.
- print >> sys.stderr, 'option', opt, \
- 'is deprecated, use -r/--regular-members-file'
elif opt in ('-w', '--welcome-msg'):
if arg.lower()[0] == 'y':
send_welcome_msg = 1
@@ -199,6 +204,8 @@ def main():
admin_notif = 0
else:
usage(1, _('Bad argument to -a/--admin-notify: %(arg)s'))
+ elif opt in ('-n', '--nomail'):
+ nomail = True
if dfile is None and nfile is None:
usage(1)
@@ -235,10 +242,10 @@ def main():
s = StringIO()
i18n.set_language(mlist.preferred_language)
if nmembers:
- addall(mlist, nmembers, 0, send_welcome_msg, s)
+ addall(mlist, nmembers, 0, send_welcome_msg, s, nomail)
if dmembers:
- addall(mlist, dmembers, 1, send_welcome_msg, s)
+ addall(mlist, dmembers, 1, send_welcome_msg, s, nomail)
if admin_notif:
realname = mlist.real_name
diff --git a/bin/list_lists b/bin/list_lists
index 870759b9..f846e699 100644
--- a/bin/list_lists
+++ b/bin/list_lists
@@ -1,6 +1,6 @@
#! @PYTHON@
#
-# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2012 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -25,6 +25,9 @@ Where:
-a / --advertised
List only those mailing lists that are publically advertised
+ -p / --public-archive
+ List only those lists with public archives.
+
--virtual-host-overview=domain
-V domain
List only those mailing lists that are homed to the given virtual
@@ -65,14 +68,15 @@ def usage(code, msg=''):
def main():
try:
- opts, args = getopt.getopt(sys.argv[1:], 'abV:h',
- ['advertised', 'bare',
+ opts, args = getopt.getopt(sys.argv[1:], 'apbV:h',
+ ['advertised', 'public-archive', 'bare',
'virtual-host-overview=',
'help'])
except getopt.error, msg:
usage(1, msg)
advertised = 0
+ public = 0
vhost = None
bare = 0
for opt, arg in opts:
@@ -80,6 +84,8 @@ def main():
usage(0)
elif opt in ('-a', '--advertised'):
advertised = 1
+ elif opt in ('-p', '--public-archive'):
+ public = 1
elif opt in ('-V', '--virtual-host-overview'):
vhost = arg
elif opt in ('-b', '--bare'):
@@ -94,6 +100,8 @@ def main():
mlist = MailList.MailList(n, lock=0)
if advertised and not mlist.advertised:
continue
+ if public and mlist.archive_private:
+ continue
if vhost and mm_cfg.VIRTUAL_HOST_OVERVIEW and \
vhost.find(mlist.web_page_url) == -1 and \
mlist.web_page_url.find(vhost) == -1:
diff --git a/bin/list_members b/bin/list_members
index d7cf9329..8995acf2 100755
--- a/bin/list_members
+++ b/bin/list_members
@@ -1,6 +1,6 @@
#! @PYTHON@
#
-# Copyright (C) 1998-2003 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2016 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -34,12 +34,6 @@ Where:
"plain" which prints just the digest members receiving that kind of
digest.
- --moderated / -m
- Print just the moderated members.
-
- --non-moderated / -M
- Print just the non-moderated members.
-
--nomail[=why] / -n [why]
Print the members that have delivery disabled. Optional argument can
be "byadmin", "byuser", "bybounce", or "unknown" which prints just the
@@ -54,6 +48,12 @@ Where:
Output member addresses case preserved the way they were added to the
list. Otherwise, addresses are printed in all lowercase.
+ --moderated / -m
+ Print just the moderated members. Ignores -r, -d, -n.
+
+ --non-moderated / -M
+ Print just the non-moderated members. Ignores -r, -d, -n.
+
--invalid / -i
Print only the addresses in the membership list that are invalid.
Ignores -r, -d, -n.
@@ -68,9 +68,8 @@ Where:
listname is the name of the mailing list to use.
-Note that if neither -r or -d is supplied, both regular members are printed
-first, followed by digest members, but no indication is given as to address
-status.
+Note that 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.
"""
import sys
@@ -181,10 +180,6 @@ def main():
preserve = True
elif opt in ('-r', '--regular'):
regular = True
- elif opt in ('-m', '--moderated'):
- moderatedonly = True
- elif opt in ('-M', '--non-moderated'):
- nonmoderatedonly = True
elif opt in ('-o', '--output'):
try:
outfile = args.pop(0)
@@ -212,10 +207,22 @@ def main():
kind = opt[i+1:]
if kind not in ('mime', 'plain'):
usage(1, _('Bad --digest option: %(kind)s'))
+ elif opt in ('-m', '--moderated'):
+ moderatedonly = True
+ if nonmoderatedonly or invalidonly or unicodeonly:
+ usage(1, _('Only one of -m, -M, -i or -u may be specified.'))
+ elif opt in ('-M', '--non-moderated'):
+ nonmoderatedonly = True
+ if moderatedonly or invalidonly or unicodeonly:
+ usage(1, _('Only one of -m, -M, -i or -u may be specified.'))
elif opt in ('-i', '--invalid'):
invalidonly = True
+ if moderatedonly or nonmoderatedonly or unicodeonly:
+ usage(1, _('Only one of -m, -M, -i or -u may be specified.'))
elif opt in ('-u', '--unicode'):
unicodeonly = True
+ if moderatedonly or nonmoderatedonly or invalidonly:
+ usage(1, _('Only one of -m, -M, -i or -u may be specified.'))
else:
# No more options left, push the last one back on the list
args.insert(0, opt)
@@ -265,7 +272,8 @@ def main():
showit = True
if moderatedonly and mlist.getMemberOption(addr, mm_cfg.Moderate):
showit = True
- if nonmoderatedonly and not mlist.getMemberOption(addr, mm_cfg.Moderate):
+ if nonmoderatedonly and not mlist.getMemberOption(addr,
+ mm_cfg.Moderate):
showit = True
if showit:
print >> fp, formataddr((safe(name), addr))
diff --git a/bin/mailmanctl b/bin/mailmanctl
index 613f909f..2c482221 100644
--- a/bin/mailmanctl
+++ b/bin/mailmanctl
@@ -1,6 +1,6 @@
#! @PYTHON@
-# Copyright (C) 2001-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2013 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -117,7 +117,7 @@ COMMASPACE = ', '
DOT = '.'
# Locking contantsa
-LOCKFILE = os.path.join(mm_cfg.LOCK_DIR, 'master-qrunner')
+LOCKFILE = os.path.join(mm_cfg.LOCK_DIR, mm_cfg.MASTER_LOCK_FILE)
# Since we wake up once per day and refresh the lock, the LOCK_LIFETIME
# needn't be (much) longer than SNOOZE. We pad it 6 hours just to be safe.
LOCK_LIFETIME = mm_cfg.days(1) + mm_cfg.hours(6)
@@ -199,7 +199,8 @@ def acquire_lock_1(force):
lock.lock(0.1)
return lock
except LockFile.TimeOutError:
- if not force:
+ # If we're not forcing or the lock can't be determined to be stale.
+ if not force or qrunner_state():
raise
# Force removal of lock first
lock._disown()
@@ -287,7 +288,7 @@ def check_privs():
# the uid/gid.
gid = grp.getgrnam(mm_cfg.MAILMAN_GROUP)[2]
uid = pwd.getpwnam(mm_cfg.MAILMAN_USER)[2]
- myuid = os.getuid()
+ myuid = os.geteuid()
if myuid == 0:
# Set the process's supplimental groups.
groups = [x[2] for x in grp.getgrall() if mm_cfg.MAILMAN_USER in x[3]]
diff --git a/bin/newlist b/bin/newlist
index c14b77f3..f80595c8 100755
--- a/bin/newlist
+++ b/bin/newlist
@@ -1,6 +1,6 @@
#! @PYTHON@
#
-# Copyright (C) 1998-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2015 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -41,6 +41,13 @@ Options:
their list has been created. This option suppresses the prompt and
notification.
+ -a/--automate
+ 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
+ listname, listadmin-addr and admin-password are all specified on the
+ command line.
+
-h/--help
Print this help text and exit.
@@ -84,8 +91,9 @@ where www.mydom.ain is used for `urlhost' but it will also be used for
'--urlhost' and '--emailhost' have precedence to this notation.
If you spell the list name as just `mylist', then the email hostname will be
-taken from DEFAULT_EMAIL_HOST and the url will be taken from DEFAULT_URL (as
-defined in your Defaults.py file or overridden by settings in mm_cfg.py).
+taken from DEFAULT_EMAIL_HOST and the url will be taken from DEFAULT_URL_HOST
+interpolated into DEFAULT_URL_PATTERN (as defined in your Defaults.py file or
+overridden by settings in mm_cfg.py).
Note that listnames are forced to lowercase.
"""
@@ -123,21 +131,24 @@ def usage(code, msg=''):
def main():
try:
- opts, args = getopt.getopt(sys.argv[1:], 'hql:u:e:',
- ['help', 'quiet', 'language=',
+ opts, args = getopt.getopt(sys.argv[1:], 'hqal:u:e:',
+ ['help', 'quiet', 'automate', 'language=',
'urlhost=', 'emailhost='])
except getopt.error, msg:
usage(1, msg)
lang = mm_cfg.DEFAULT_SERVER_LANGUAGE
- quiet = 0
+ quiet = False
+ automate = False
urlhost = None
emailhost = None
for opt, arg in opts:
if opt in ('-h', '--help'):
usage(0)
if opt in ('-q', '--quiet'):
- quiet = 1
+ quiet = True
+ if opt in ('-a', '--automate'):
+ automate = True
if opt in ('-l', '--language'):
lang = arg
if opt in ('-u', '--urlhost'):
@@ -204,7 +215,9 @@ def main():
except Errors.BadListNameError, s:
usage(1, _('Illegal list name: %(s)s'))
except Errors.EmailAddressError, s:
- usage(1, _('Bad owner email address: %(s)s'))
+ usage(1, _('Bad owner email address: %(s)s') +
+ _(' - owner addresses need to be fully-qualified names'
+ ' like "owner@example.com", not just "owner".'))
except Errors.MMListAlreadyExistsError:
usage(1, _('List already exists: %(listname)s'))
@@ -226,9 +239,10 @@ def main():
sys.modules[modname].create(mlist)
# And send the notice to the list owner
- if not quiet:
+ if not quiet and not automate:
print _('Hit enter to notify %(listname)s owner...'),
sys.stdin.readline()
+ if not quiet:
siteowner = Utils.get_site_email(mlist.host_name, 'owner')
text = Utils.maketext(
'newlist.txt',
diff --git a/bin/rmlist b/bin/rmlist
index f61b41d4..b0980104 100755
--- a/bin/rmlist
+++ b/bin/rmlist
@@ -134,7 +134,7 @@ def main():
# Remove any held messages for this list
for filename in os.listdir(mm_cfg.DATA_DIR):
- cre = re.compile('^heldmsg-%s-\d+\.(pck|txt)$' % listname,
+ cre = re.compile('^heldmsg-%s-\d+\.(pck|txt)$' % re.escape(listname),
re.IGNORECASE)
if cre.match(filename):
REMOVABLES.append((os.path.join(mm_cfg.DATA_DIR, filename),
diff --git a/bin/sync_members b/bin/sync_members
index 13d0b2b0..0c860d25 100755
--- a/bin/sync_members
+++ b/bin/sync_members
@@ -1,6 +1,6 @@
#! @PYTHON@
#
-# Copyright (C) 1998-2003 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2013 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -256,6 +256,10 @@ def main():
try:
if not dryrun:
mlist.ApprovedAddMember(userdesc, welcome, notifyadmin)
+ # Avoid UnicodeError if name can't be decoded
+ if isinstance(name, str):
+ name = unicode(name, errors='replace')
+ name = name.encode(enc, 'replace')
s = email.Utils.formataddr((name, addr)).encode(enc, 'replace')
print _('Added : %(s)s')
except Errors.MMAlreadyAMember:
@@ -276,6 +280,10 @@ def main():
# reasons is in the database. Use a lower level remove to
# get rid of this member's entry
mlist.removeMember(addr)
+ # Avoid UnicodeError if name can't be decoded
+ if isinstance(name, str):
+ name = unicode(name, errors='replace')
+ name = name.encode(enc, 'replace')
s = email.Utils.formataddr((name, addr)).encode(enc, 'replace')
print _('Removed: %(s)s')