diff options
Diffstat (limited to '')
-rwxr-xr-x | bin/add_members | 43 | ||||
-rw-r--r-- | bin/list_lists | 14 | ||||
-rwxr-xr-x | bin/list_members | 38 | ||||
-rw-r--r-- | bin/mailmanctl | 9 | ||||
-rwxr-xr-x | bin/newlist | 32 | ||||
-rwxr-xr-x | bin/rmlist | 2 | ||||
-rwxr-xr-x | bin/sync_members | 10 |
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', @@ -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') |