/usr/lib/mailman/cron/mailpasswds is in mailman 1:2.1.14-3ubuntu0.4.
This file is owned by root:list, with mode 0o755.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | #! /usr/bin/python
#
# 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
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
"""Send password reminders for all lists to all users.
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.
Usage: %(PROGRAM)s [options]
Options:
-l listname
--listname=listname
Send password reminders for the named list only. If omitted,
reminders are sent for all lists. Multiple -l/--listname options are
allowed.
-h/--help
Print this message and exit.
"""
# This puppy should probably do lots of logging.
import sys
import os
import errno
import getopt
from types import UnicodeType
import paths
# mm_cfg must be imported before the other modules, due to the side-effect of
# it hacking sys.paths to include site-packages. Without this, running this
# script from cron with python -S will fail.
from Mailman import mm_cfg
from Mailman import MailList
from Mailman import Errors
from Mailman import Utils
from Mailman import Message
from Mailman import i18n
from Mailman.Logging.Syslog import syslog
# Work around known problems with some RedHat cron daemons
import signal
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
NL = '\n'
PROGRAM = sys.argv[0]
_ = i18n._
def usage(code, msg=''):
if code:
fd = sys.stderr
else:
fd = sys.stdout
print >> fd, _(__doc__)
if msg:
print >> fd, msg
sys.exit(code)
def tounicode(s, enc):
if isinstance(s, UnicodeType):
return s
return unicode(s, enc, 'replace')
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], 'l:h',
['listname=', 'help'])
except getopt.error, msg:
usage(1, msg)
if args:
usage(1)
listnames = None
for opt, arg in opts:
if opt in ('-h', '--help'):
usage(0)
if opt in ('-l', '--listname'):
if listnames is None:
listnames = [arg]
else:
listnames.append(arg)
if listnames is None:
listnames = Utils.list_names()
# 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.
try:
sitelist = MailList.MailList(mm_cfg.MAILMAN_SITE_LIST, lock=0)
except Errors.MMUnknownListError:
# Do it this way for I18n's _()
sitelistname = mm_cfg.MAILMAN_SITE_LIST
print >> sys.stderr, _('Site list is missing: %(sitelistname)s')
syslog('error', 'Site list is missing: %s', mm_cfg.MAILMAN_SITE_LIST)
sys.exit(1)
# Group lists by host_name if VIRTUAL_HOST_OVERVIEW is true, otherwise
# there's only one key in this dictionary: mm_cfg.DEFAULT_EMAIL_HOST. The
# values are lists of the unlocked MailList instances.
byhost = {}
for listname in listnames:
mlist = MailList.MailList(listname, lock=0)
if not mlist.send_reminders:
continue
if mm_cfg.VIRTUAL_HOST_OVERVIEW:
host = mlist.host_name
else:
# See the note in Defaults.py concerning DEFAULT_HOST_NAME
# vs. DEFAULT_EMAIL_HOST.
host = mm_cfg.DEFAULT_HOST_NAME or mm_cfg.DEFAULT_EMAIL_HOST
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'
userinfo = {}
for mlist in byhost[host]:
listaddr = mlist.GetListEmail()
for member in mlist.getMembers():
# The user may have disabled reminders for this list
if mlist.getMemberOption(member,
mm_cfg.SuppressPasswordReminder):
continue
# Group by the lower-cased address, since Mailman always
# treates person@dom.ain the same as PERSON@dom.ain.
try:
password = mlist.getMemberPassword(member)
except Errors.NotAMemberError:
# Here's a member with no passwords, which I think was
# possible in older versions of Mailman. Log this and
# move on.
syslog('error', 'password-less member %s for list %s',
member, mlist.internal_name())
continue
optionsurl = mlist.GetOptionsURL(member)
lang = mlist.getMemberLanguage(member)
info = (listaddr, password, optionsurl, lang)
userinfo.setdefault(member, []).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
enc = Utils.GetCharSet(poplang)
# Now we're finally ready to send the email!
siteowner = Utils.get_site_email(host, 'owner')
sitereq = Utils.get_site_email(host, 'request')
sitebounce = Utils.get_site_email(host, 'bounces')
text = Utils.maketext(
'cronpass.txt',
{'hostname': host,
'useraddr': addr,
'exreq' : sitereq,
'owner' : siteowner,
}, lang=poplang)
# Coerce everything to Unicode
text = tounicode(text, enc)
table = [tounicode(_t, enc) for _t in table]
# Translate the message and headers to user's suggested lang
otrans = i18n.get_translation()
try:
i18n.set_language(poplang)
# Craft table header after language was set
header = '%-40s %-10s\n%-40s %-10s' % (
_('List'), _('Password // URL'), '----', '--------')
header = tounicode(header, enc)
# Add the table to the end so it doesn't get wrapped/filled
text += (header + '\n' + NL.join(table))
msg = Message.UserNotification(
addr, siteowner,
_('%(host)s mailing list memberships reminder'),
text.encode(enc, 'replace'), poplang)
# Note that text must be encoded into 'enc' because unicode
# cause error within email module in some language (Japanese).
finally:
i18n.set_translation(otrans)
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': sitebounce,
'_nolist' : 1,
'verp' : mm_cfg.VERP_PASSWORD_REMINDERS,
})
if __name__ == '__main__':
main()
|