aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbwarsaw <>2003-03-31 20:07:57 +0000
committerbwarsaw <>2003-03-31 20:07:57 +0000
commitd258eeb3b86fc7a084e68428f3354789ef8e3b4c (patch)
treec5e669fcefd0fa98790159734d09c0d37d63c204
parent5a6aeea41cd141bc4e962381284cbeeefa7bf4d9 (diff)
downloadmailman2-d258eeb3b86fc7a084e68428f3354789ef8e3b4c.tar.gz
mailman2-d258eeb3b86fc7a084e68428f3354789ef8e3b4c.tar.xz
mailman2-d258eeb3b86fc7a084e68428f3354789ef8e3b4c.zip
Backporting various fixes and improvements from the trunk.
-rwxr-xr-xbin/add_members22
-rwxr-xr-xbin/check_perms45
-rw-r--r--bin/genaliases52
-rwxr-xr-xbin/list_members26
-rwxr-xr-xbin/remove_members51
-rwxr-xr-xbin/transcheck15
6 files changed, 130 insertions, 81 deletions
diff --git a/bin/add_members b/bin/add_members
index ad4f43b8..92b3ca04 100755
--- a/bin/add_members
+++ b/bin/add_members
@@ -1,19 +1,19 @@
#! @PYTHON@
#
-# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2003 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
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
-#
+#
# This program 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 this program; if not, write to the Free Software
+# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# argv[1] should be the name of the list.
@@ -38,13 +38,13 @@ Options:
-n/--non-digest-members-file are deprecated synonyms for this option.
--digest-members-file=file
- -d=file
+ -d file
Similar to above, but these people become digest members.
--changes-msg=<y|n>
- -c <y|n>
- Set whether or not to send the list members the `there's going to be
- big changes to your list' message. defaults to no.
+ -c <y|n>
+ Set whether or not to send the list members the `there's going to be
+ big changes to your list' message. defaults to no.
--welcome-msg=<y|n>
-w <y|n>
@@ -174,7 +174,7 @@ def addall(mlist, members, digest, ack, outfp):
print >> tee, _('Hostile address (illegal characters): %(member)s')
else:
print >> tee, _('Subscribed: %(member)s')
-
+
def main():
@@ -186,7 +186,7 @@ def main():
'non-digest-members-file=',
'digest-members-file=',
'changes-msg=',
- 'welcome-msg=',
+ 'welcome-msg=',
'help'])
except getopt.error, msg:
usage(1, msg)
@@ -235,7 +235,7 @@ def main():
admin_notif = 0
else:
usage(1, _('Bad argument to -a/--admin-notify: %(arg)s'))
-
+
if dfile is None and nfile is None:
usage(1)
diff --git a/bin/check_perms b/bin/check_perms
index 44fbe547..4f976b06 100755
--- a/bin/check_perms
+++ b/bin/check_perms
@@ -26,12 +26,12 @@ permission problems found. With -v be verbose.
"""
-import sys
import os
-import errno
-import getopt
+import sys
import pwd
import grp
+import errno
+import getopt
from stat import *
try:
@@ -55,11 +55,17 @@ PROGRAM = sys.argv[0]
# Gotta check the archives/private/*/database/* files
+try:
+ True, False
+except NameError:
+ True = 1
+ False = 0
+
class State:
- FIX = 0
- VERBOSE = 0
+ FIX = False
+ VERBOSE = False
ERRORS = 0
STATE = State()
@@ -78,7 +84,13 @@ def statgidmode(path):
stat = os.stat(path)
return stat[ST_MODE], stat[ST_GID]
+seen = {}
+
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:
@@ -151,10 +163,16 @@ def checkall():
prefix = mm_cfg.PREFIX
print _('checking mode for %(prefix)s')
dirs = {}
- for d in (mm_cfg.PREFIX, mm_cfg.EXEC_PREFIX, mm_cfg.VAR_PREFIX):
- dirs[d] = 1
+ for d in (mm_cfg.PREFIX, mm_cfg.EXEC_PREFIX, mm_cfg.VAR_PREFIX,
+ mm_cfg.LOG_DIR):
+ dirs[d] = True
for d in dirs.keys():
- mode = statmode(d)
+ try:
+ mode = statmode(d)
+ except OSError, e:
+ if e.errno <> errno.ENOENT: raise
+ print _('WARNING: directory does not exist: %(d)s')
+ continue
if (mode & DIRPERMS) <> DIRPERMS:
STATE.ERRORS += 1
print _('directory must be at least 02775: %(d)s'),
@@ -166,7 +184,6 @@ def checkall():
# check all subdirs
os.path.walk(d, checkwalk, STATE)
-
def checkarchives():
private = mm_cfg.PRIVATE_ARCHIVE_FILE_DIR
if STATE.VERBOSE:
@@ -185,7 +202,6 @@ def checkarchives():
MBOXPERMS = S_IRGRP | S_IWGRP | S_IRUSR | S_IWUSR
-
def checkmboxfile(mboxdir):
absdir = os.path.join(mm_cfg.PRIVATE_ARCHIVE_FILE_DIR, mboxdir)
for f in os.listdir(absdir):
@@ -202,7 +218,6 @@ def checkmboxfile(mboxdir):
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
@@ -226,7 +241,6 @@ def checkarchivedbs():
else:
print
-
def checkcgi():
cgidir = os.path.join(mm_cfg.EXEC_PREFIX, 'cgi-bin')
if STATE.VERBOSE:
@@ -332,8 +346,7 @@ def usage(code, msg=''):
if __name__ == '__main__':
try:
- opts, args = getopt.getopt(sys.argv[1:],
- 'fvh',
+ opts, args = getopt.getopt(sys.argv[1:], 'fvh',
['fix', 'verbose', 'help'])
except getopt.error, msg:
usage(1, msg)
@@ -342,9 +355,9 @@ if __name__ == '__main__':
if opt in ('-h', '--help'):
usage(0)
elif opt in ('-f', '--fix'):
- STATE.FIX = 1
+ STATE.FIX = True
elif opt in ('-v', '--verbose'):
- STATE.VERBOSE = 1
+ STATE.VERBOSE = True
checkall()
checkarchives()
diff --git a/bin/genaliases b/bin/genaliases
index 289c5bec..bda43ef5 100644
--- a/bin/genaliases
+++ b/bin/genaliases
@@ -1,37 +1,40 @@
#! @PYTHON@
#
-# Copyright (C) 2001,2002 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2003 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
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
-#
+#
# This program 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 this program; if not, write to the Free Software
+# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-"""Regenerate Postfix's data/aliases and data/aliases.db files from scratch.
+"""Regenerate Mailman specific aliases from scratch.
-Usage:
-
- genaliases [options]
+The actual output depends on the value of the `MTA' variable in your mm_cfg.py
+file.
+Usage: genaliases [options]
Options:
+ -q/--quiet
+ Some MTA output can include more verbose help text. Use this to tone
+ down the verbosity.
+
-h/--help
Print this message and exit.
"""
-import sys
import os
+import sys
import getopt
-import fcntl
import paths # path hacking
from Mailman import mm_cfg
@@ -39,6 +42,12 @@ from Mailman import Utils
from Mailman import MailList
from Mailman.i18n import _
+try:
+ True, False
+except NameError:
+ True = 1
+ False = 0
+
def usage(code, msg=''):
@@ -54,32 +63,35 @@ def usage(code, msg=''):
def main():
+ quiet = False
try:
- opts, args = getopt.getopt(sys.argv[1:], 'h', ['help'])
+ opts, args = getopt.getopt(sys.argv[1:], 'hq',
+ ['help', 'quiet'])
except getopt.error, msg:
usage(1, msg)
for opt, arg in opts:
if opt in ('-h', '--help'):
usage(0)
+ elif opt in ('-q', '--quiet'):
+ quiet = True
if args:
usage(1)
- # Open up the MTA specific module
+ # Import the MTA specific module
modulename = 'Mailman.MTA.' + mm_cfg.MTA
__import__(modulename)
MTA = sys.modules[modulename]
- # Open the text file and Berkeley DB files, truncating any data already
- # there. We need to acquire a lock so nobody tries to update the files
- # while we're doing it.
+ # We need to acquire a lock so nobody tries to update the files while
+ # we're doing it.
lock = MTA.makelock()
lock.lock()
# Group lists by virtual hostname
mlists = {}
for listname in Utils.list_names():
- mlist = MailList.MailList(listname, lock=0)
+ mlist = MailList.MailList(listname, lock=False)
mlists.setdefault(mlist.host_name, []).append(mlist)
# Make sure the files are created rw-rw-xxx; it should be okay to be world
# readable.
@@ -87,14 +99,16 @@ def main():
try:
MTA.clear()
if not mlists:
- MTA.create(None, nolock=1)
+ MTA.create(None, nolock=True, quiet=quiet)
else:
for hostname, vlists in mlists.items():
for mlist in vlists:
- MTA.create(mlist, nolock=1)
+ MTA.create(mlist, nolock=True, quiet=quiet)
+ # Be verbose for only the first printed list
+ quiet = True
finally:
os.umask(omask)
- lock.unlock(unconditionally=1)
+ lock.unlock(unconditionally=True)
diff --git a/bin/list_members b/bin/list_members
index 3ad10f51..b4fdbb27 100755
--- a/bin/list_members
+++ b/bin/list_members
@@ -1,6 +1,6 @@
#! @PYTHON@
#
-# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2003 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
@@ -61,6 +61,7 @@ status.
"""
import sys
+from types import UnicodeType
import paths
from Mailman import mm_cfg
@@ -72,6 +73,8 @@ from Mailman.i18n import _
from email.Utils import formataddr
PROGRAM = sys.argv[0]
+ENC = sys.getdefaultencoding()
+
WHYCHOICES = {'enabled' : MemberAdaptor.ENABLED,
'unknown' : MemberAdaptor.UNKNOWN,
'byuser' : MemberAdaptor.BYUSER,
@@ -92,6 +95,15 @@ def usage(code, msg=''):
+def safe(s):
+ if not s:
+ return ''
+ if isinstance(s, UnicodeType):
+ return s.encode(ENC, 'replace')
+ return unicode(s, ENC, 'replace').encode(ENC, 'replace')
+
+
+
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
@@ -199,17 +211,15 @@ def main():
if regular:
rmembers.sort()
for addr in rmembers:
- name = fullnames and mlist.getMemberName(addr)
+ name = fullnames and mlist.getMemberName(addr) or ''
# Filter out nomails
if nomail and not whymatches(mlist, addr, why):
continue
- enc = sys.getdefaultencoding()
- s = formataddr((name, addr)).encode(enc, 'replace')
- print >> fp, s
+ print >> fp, formataddr((safe(name), addr))
if digest:
dmembers.sort()
for addr in dmembers:
- name = fullnames and mlist.getMemberName(addr)
+ name = fullnames and mlist.getMemberName(addr) or ''
# Filter out nomails
if nomail and not whymatches(mlist, addr, why):
continue
@@ -222,9 +232,7 @@ def main():
# They're getting MIME digests
if kind == 'plain':
continue
- enc = sys.getdefaultencoding()
- s = formataddr((name, addr)).encode(enc, 'replace')
- print >> fp, s
+ print >> fp, formataddr((safe(name), addr))
diff --git a/bin/remove_members b/bin/remove_members
index 888dfa51..b8f92f5e 100755
--- a/bin/remove_members
+++ b/bin/remove_members
@@ -1,6 +1,6 @@
#! @PYTHON@
#
-# Copyright (C) 1998,1999,2000,2001,2002 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2003 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
@@ -31,21 +31,23 @@ Options:
--all
-a
Remove all members of the mailing list.
- (mutually exclusive with --fromall)
+ (mutually exclusive with --fromall)
--fromall
- Removes the given addresses from all the lists on this system
- regardless of virtual domains if you have any. This option cannot be
- used -a/--all. Also, you should not specify a listname when using this
- option.
+ Removes the given addresses from all the lists on this system
+ regardless of virtual domains if you have any. This option cannot be
+ used -a/--all. Also, you should not specify a listname when using
+ this option.
--nouserack
-n
- Don't send the user acknowledgements.
+ Don't send the user acknowledgements. If not specified, the list
+ default value is used.
--noadminack
-N
- Don't send the admin acknowledgements.
+ Don't send the admin acknowledgements. If not specified, the list
+ default value is used.
--help
-h
@@ -66,6 +68,12 @@ from Mailman import Utils
from Mailman import Errors
from Mailman.i18n import _
+try:
+ True, False
+except NameError:
+ True = 1
+ False = 0
+
def usage(code, msg=''):
@@ -83,10 +91,10 @@ def ReadFile(filename):
lines = []
if filename == "-":
fp = sys.stdin
- closep = 0
+ closep = False
else:
fp = open(filename)
- closep = 1
+ closep = True
lines = filter(None, [line.strip() for line in fp.readlines()])
if closep:
fp.close()
@@ -106,10 +114,11 @@ def main():
usage(1)
filename = None
- all = 0
- alllists = 0
- userack = 1
- admin_notif = 1
+ all = False
+ alllists = False
+ # None means use list default
+ userack = None
+ admin_notif = None
for opt, arg in opts:
if opt in ('-h', '--help'):
@@ -117,13 +126,13 @@ def main():
elif opt in ('-f', '--file'):
filename = arg
elif opt in ('-a', '--all'):
- all = 1
+ all = True
elif opt == '--fromall':
- alllists = 1
+ alllists = True
elif opt in ('-n', '--nouserack'):
- userack = 0
+ userack = False
elif opt in ('-N', '--noadminack'):
- admin_notif = 0
+ admin_notif = False
# You probably don't want to delete all the users of all the lists -- Marc
if all and alllists:
@@ -163,10 +172,8 @@ def main():
if not alllists:
print _('No such member: %(addr)s')
continue
- mlist.ApprovedDeleteMember(addr,
- 'bin/remove_members',
- admin_notif,
- userack)
+ mlist.ApprovedDeleteMember(addr, 'bin/remove_members',
+ admin_notif, userack)
if alllists:
print _("User `%(addr)s' removed from list: %(listname)s.")
mlist.Save()
diff --git a/bin/transcheck b/bin/transcheck
index fbb0dcd8..78d1b6d1 100755
--- a/bin/transcheck
+++ b/bin/transcheck
@@ -55,14 +55,19 @@ def usage(code, msg=''):
class TransChecker:
"check a translation comparing with the original string"
- def __init__(self, regexp):
+ def __init__(self, regexp, escaped=None):
self.dict = {}
self.errs = []
self.regexp = re.compile(regexp)
+ self.escaped = None
+ if escaped:
+ self.escaped = re.compile(escaped)
def checkin(self, string):
"scan a string from the original file"
for key in self.regexp.findall(string):
+ if self.escaped and self.escaped.match(key):
+ continue
if self.dict.has_key(key):
self.dict[key] += 1
else:
@@ -71,6 +76,8 @@ class TransChecker:
def checkout(self, string):
"scan a translated string"
for key in self.regexp.findall(string):
+ if self.escaped and self.escaped.match(key):
+ continue
if self.dict.has_key(key):
self.dict[key] -= 1
else:
@@ -273,9 +280,9 @@ def check_file(translatedFile, originalFile, html=0, quiet=0):
search also <MM-*> tags if html is not zero"""
if html:
- c = TransChecker("(%\([^)]+\)[0-9]*[sd]|</?MM-[^>]+>)")
+ c = TransChecker("(%%|%\([^)]+\)[0-9]*[sd]|</?MM-[^>]+>)", "^%%$")
else:
- c = TransChecker("(%\([^)]+\)[0-9]*[sd])")
+ c = TransChecker("(%%|%\([^)]+\)[0-9]*[sd])", "^%%$")
try:
f = open(originalFile)
@@ -319,7 +326,7 @@ def check_po(file, quiet=0):
"scan the po file comparing msgids with msgstrs"
n = 0
p = POParser(file)
- c = TransChecker("(%\([^)]+\)[0-9]*[sdu]|%[0-9]*[sdu])")
+ c = TransChecker("(%%|%\([^)]+\)[0-9]*[sdu]|%[0-9]*[sdu])", "^%%$")
while p.parse():
if p.msgstr:
c.reset()