diff options
| author | bwarsaw | 2001-08-04 05:23:56 +0000 |
|---|---|---|
| committer | bwarsaw | 2001-08-04 05:23:56 +0000 |
| commit | 270c49ac6ec96aa93ac39ca6bff668a847bc9018 (patch) | |
| tree | b3f6404f6933ae32eefbac9f7a211fa9c7a00a2f | |
| parent | c03e27380edf2dddf07da60ec00422915806f8b1 (diff) | |
| download | mailman-270c49ac6ec96aa93ac39ca6bff668a847bc9018.tar.gz mailman-270c49ac6ec96aa93ac39ca6bff668a847bc9018.tar.zst mailman-270c49ac6ec96aa93ac39ca6bff668a847bc9018.zip | |
Massively rewritten so as to (hopefully) simplify the logic, and not
send the password reminder as if it's coming from the "first" public
mailing list. Instead it comes from the "site list".
| -rwxr-xr-x | cron/mailpasswds | 209 |
1 files changed, 109 insertions, 100 deletions
diff --git a/cron/mailpasswds b/cron/mailpasswds index b1e2851c8..4ed8e009f 100755 --- a/cron/mailpasswds +++ b/cron/mailpasswds @@ -18,25 +18,24 @@ """Send password reminders for all lists to all users. -Any arguments are taken as a list of addresses that become the focus - only -the subscribers on the list are attended to, all other subscribers are -ignored. In addition, if any addresses are specified, a line is printed for -each list where that address is found. (Otherwise operation is silent.) +This program scans all mailing lists and collects users and their passwords, +grouped by the list's host_name if mm_cfg.VIRTUAL_HOST_OVERVIEW is true. Then +one email message is sent to each unique user (per-virtual host) containing +the list passwords and options url for the user. The password reminder comes +from the mm_cfg.MAILMAN_SITE_LIST, which must exist. -We accumulate users and their passwords, and use the last list to send a -single message to each user with their complete collection of passwords, -rather than sending a single message for each password. - -If mm_cfg.VIRTUAL_HOST_OVERVIEW is true, we further group users by the virtual -host the mailing lists are assigned to. This is so that virtual domains are -treated like real separate machines. +Usage: %(PROGRAM)s [options] +Options: + -h/--help + Print this message and exit. """ # This puppy should probably do lots of logging. import sys import os import errno +import getopt import paths # mm_cfg must be imported before the other modules, due to the side-effect of @@ -53,110 +52,120 @@ import signal signal.signal(signal.SIGCHLD, signal.SIG_DFL) NL = '\n' +PROGRAM = sys.argv[0] -def mail_passwords(mlist, hosts): - """Send each user their complete list of passwords. - - The list can be any random one - it is only used for the message - delivery mechanism. Users are grouped by virtual host. - """ - mailman_owner = mm_cfg.MAILMAN_OWNER - for host, users in hosts.items(): - subj = _(' %(host)s mailing list memberships reminder') - for addr, data in users.items(): - table = [] - for l, r, p, u in data: - if len(l) > 39: - table.append("%s\n %-10s\n%s\n" % (l, p, u)) - else: - table.append("%-40s %-10s\n%s\n" % (l, p, u)) - header = ("%-40s %-10s\n%-40s %-10s" - % (_('List'), _('Password // URL'), "----", "--------")) - text = Utils.maketext( - 'cronpass.txt', - {'hostname': host, - 'useraddr': addr, - 'exreq' : r, - 'owner' : mailman_owner, - }, mlist.preferred_language) - # add this to the end so it doesn't get wrapped/filled - text = text + header + '\n' + NL.join(table) - msg = Message.UserNotification(addr, mailman_owner, subj, text) - msg['X-No-Archive'] = 'yes' - msg.send(mlist, **{'_nolist': 1}) +def usage(code, msg=''): + print >> sys.stderr, _(__doc__) + if msg: + print >> sys.stderr, msg + sys.exit(code) def main(): - """Consolidate all the list/url/password info for each user, so we send - the user a single message with the info for all their lists on this - site. - """ - # constrain to specified lists, if any - confined_to = sys.argv[1:] - # Use this list for message delivery only - a_public_list = None - # Group lists by the assigned virtual host, if - # mm_cfg.VIRTUAL_HOST_OVERVIEW is true. Otherwise, there's only one key - # in this dictionary: mm_cfg.DEFAULT_HOST_NAME. Each entry in this - # dictionary is a dictionary of user email addresses - hosts = {} + try: + opts, args = getopt.getopt(sys.argv[1:], 'h', ['help']) + except getopt.error, msg: + usage(1, msg) + + if args: + usage(1) + + for opt, arg in opts: + if opt in ('-h', '--help'): + usage(0) + + # This is the list that all the reminders will look like they come from, + # but with the host name coerced to the virtual host we're processing. + sitelist = MailList.MailList(mm_cfg.MAILMAN_SITE_LIST, lock=0) + + # Group lists by host_name if VIRTUAL_HOST_OVERVIEW is true, otherwise + # there's only one key in this dictionary: mm_cfg.DEFAULT_HOST_NAME. The + # values are lists of the unlocked MailList instances. + byhost = {} for listname in Utils.list_names(): - if confined_to and listname not in confined_to: - continue -## else: -## print 'Processing list:', listname mlist = MailList.MailList(listname, lock=0) - if not a_public_list and mlist.advertised: - a_public_list = mlist if not mlist.send_reminders: continue - listaddr = mlist.GetListEmail() - listreq = mlist.GetRequestEmail() - umbrella = mlist.umbrella_list - # get host information if mm_cfg.VIRTUAL_HOST_OVERVIEW: host = mlist.host_name else: host = mm_cfg.DEFAULT_HOST_NAME - # - # each entry in this dictionary is a list of tuples of the following - # form: (listaddr, listreq, password, url) - users = hosts.setdefault(host, {}) - badaddrs = [] - for addr, passwd in mlist.passwords.items(): - # The user may have disabled password reminders for this list - if mlist.getMemberOption(addr, mm_cfg.SuppressPasswordReminder): - continue - url = mlist.GetOptionsURL(addr, absolute=1) - realaddr = mlist.getMemberCPAddress(addr) - if not realaddr: - badaddrs.append(addr) - continue - recip = mlist.GetMemberAdminEmail(realaddr) - userinfo = (listaddr, listreq, passwd, url) - infolist = users.get(recip, []) - infolist.append(userinfo) - users[recip] = infolist - # were there any addresses that are in the password dictionary but are - # not subscribed? - if badaddrs: - mlist.Lock() - try: - for addr in badaddrs: - del mlist.passwords[addr] - mlist.Save() - finally: - mlist.Unlock() - if a_public_list: - a_public_list.Lock() - try: - mail_passwords(a_public_list, hosts) - finally: - a_public_list.Save() - a_public_list.Unlock() + byhost.setdefault(host, []).append(mlist) + + # Now for each virtual host, collate the user information. Each user + # entry has the form (listaddr, password, optionsurl) + for host in byhost.keys(): + # Site owner is `mailman@dom.ain' + subj = _('%(host)s mailing list memberships reminder') + userinfo = {} + for mlist in byhost[host]: + listaddr = mlist.GetListEmail() + for member in mlist.getMembers(): + # BAW: we group by cpaddress because although it's highly + # likely, there's no guarantee that person@list1 is the same + # as PERSON@list2. Sigh. + cpaddress = mlist.getMemberCPAddress(member) + password = mlist.getMemberPassword(member) + optionsurl = mlist.GetOptionsURL(member) + lang = mlist.getMemberLanguage(member) + info = (listaddr, password, optionsurl, lang) + userinfo.setdefault(cpaddress, []).append(info) + # Now that we've collected user information for this host, send each + # user the password reminder. + for addr in userinfo.keys(): + # If the person is on more than one list, it is possible that they + # have different preferred languages, and there's no good way to + # know which one they want their password reminder in. Pick the + # most popular, and break the tie randomly. + # + # Also, we need an example -request address for cronpass.txt and + # again, there's no clear winner. Just take the first one in this + # case. + table = [] + langs = {} + for listaddr, password, optionsurl, lang in userinfo[addr]: + langs[lang] = langs.get(lang, 0) + 1 + # If the list address is really long, break it across two + # lines. + if len(listaddr) > 39: + fmt = '%s\n %-10s\n%s\n' + else: + fmt = '%-40s %-10s\n%s\n' + table.append(fmt % (listaddr, password, optionsurl)) + # Figure out which language to use + langcnt = 0 + poplang = None + for lang, cnt in langs.items(): + if cnt > langcnt: + poplang = lang + langcnt = cnt + # Craft the table header + header = '%-40s %-10s\n%-40s %-10s' % ( + _('List'), _('Password // URL'), '----', '--------') + # Now we're finally ready to send the email! + siteadmin = Utils.get_site_email(host, '-admin') + sitereq = Utils.get_site_email(host, '-request') + text = Utils.maketext( + 'cronpass.txt', + {'hostname': host, + 'useraddr': addr, + 'exreq' : sitereq, + 'owner' : siteadmin, + }, lang=poplang) + # Add the table to the end so it doesn't get wrapped/filled + text += (header + '\n' + NL.join(table)) + msg = Message.UserNotification(addr, sitereq, subj, text) + msg['X-No-Archive'] = 'yes' + # We want to make this look like it's coming from the siteowner's + # list, but we also want to be sure that the apparent host name is + # the current virtual host. Look in CookHeaders.py for why this + # trick works. Blarg. + msg.send(sitelist, **{'errorsto': sitereq, + '_nolist' : 1, + }) |
