~tgc/mailman/2.1-da

1 by
This commit was manufactured by cvs2svn to create branch
1
#! @PYTHON@
2
#
6 by bwarsaw
main(): Treat addresses case-insensitively for grouping purposes.
3
# Copyright (C) 1998-2003 by the Free Software Foundation, Inc.
1 by
This commit was manufactured by cvs2svn to create branch
4
#
5
# This program is free software; you can redistribute it and/or
6
# modify it under the terms of the GNU General Public License
7
# as published by the Free Software Foundation; either version 2
8
# of the License, or (at your option) any later version.
6 by bwarsaw
main(): Treat addresses case-insensitively for grouping purposes.
9
#
1 by
This commit was manufactured by cvs2svn to create branch
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
# GNU General Public License for more details.
6 by bwarsaw
main(): Treat addresses case-insensitively for grouping purposes.
14
#
1 by
This commit was manufactured by cvs2svn to create branch
15
# You should have received a copy of the GNU General Public License
6 by bwarsaw
main(): Treat addresses case-insensitively for grouping purposes.
16
# along with this program; if not, write to the Free Software
749 by tkikuchi
FSF office has moved to 51 Franklin Street.
17
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1 by
This commit was manufactured by cvs2svn to create branch
18
19
"""Send password reminders for all lists to all users.
20
21
This program scans all mailing lists and collects users and their passwords,
22
grouped by the list's host_name if mm_cfg.VIRTUAL_HOST_OVERVIEW is true.  Then
23
one email message is sent to each unique user (per-virtual host) containing
24
the list passwords and options url for the user.  The password reminder comes
25
from the mm_cfg.MAILMAN_SITE_LIST, which must exist.
26
27
Usage: %(PROGRAM)s [options]
28
29
Options:
30
    -l listname
31
    --listname=listname
32
        Send password reminders for the named list only.  If omitted,
33
        reminders are sent for all lists.  Multiple -l/--listname options are
34
        allowed.
35
36
    -h/--help
37
        Print this message and exit.
38
"""
39
40
# This puppy should probably do lots of logging.
41
import sys
42
import os
43
import errno
44
import getopt
81 by bwarsaw
Backporting from the HEAD -- bin and cron scripts
45
from types import UnicodeType
1 by
This commit was manufactured by cvs2svn to create branch
46
47
import paths
48
# mm_cfg must be imported before the other modules, due to the side-effect of
49
# it hacking sys.paths to include site-packages.  Without this, running this
50
# script from cron with python -S will fail.
51
from Mailman import mm_cfg
52
from Mailman import MailList
53
from Mailman import Errors
54
from Mailman import Utils
55
from Mailman import Message
56
from Mailman import i18n
57
from Mailman.Logging.Syslog import syslog
58
59
# Work around known problems with some RedHat cron daemons
60
import signal
61
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
62
63
NL = '\n'
64
PROGRAM = sys.argv[0]
65
66
_ = i18n._
67
68
69

70
def usage(code, msg=''):
71
    if code:
72
        fd = sys.stderr
73
    else:
74
        fd = sys.stdout
75
    print >> fd, _(__doc__)
76
    if msg:
77
        print >> fd, msg
78
    sys.exit(code)
79
80
81

81 by bwarsaw
Backporting from the HEAD -- bin and cron scripts
82
def tounicode(s, enc):
83
    if isinstance(s, UnicodeType):
84
        return s
85
    return unicode(s, enc, 'replace')
86
87
88

1 by
This commit was manufactured by cvs2svn to create branch
89
def main():
90
    try:
91
        opts, args = getopt.getopt(sys.argv[1:], 'l:h',
92
                                   ['listname=', 'help'])
93
    except getopt.error, msg:
94
        usage(1, msg)
95
96
    if args:
97
        usage(1)
98
99
    listnames = None
100
    for opt, arg in opts:
101
        if opt in ('-h', '--help'):
102
            usage(0)
103
        if opt in ('-l', '--listname'):
104
            if listnames is None:
105
                listnames = [arg]
106
            else:
107
                listnames.append(arg)
108
109
    if listnames is None:
110
        listnames = Utils.list_names()
111
112
    # This is the list that all the reminders will look like they come from,
113
    # but with the host name coerced to the virtual host we're processing.
114
    try:
115
        sitelist = MailList.MailList(mm_cfg.MAILMAN_SITE_LIST, lock=0)
116
    except Errors.MMUnknownListError:
117
        # Do it this way for I18n's _()
118
        sitelistname = mm_cfg.MAILMAN_SITE_LIST
119
        print >> sys.stderr, _('Site list is missing: %(sitelistname)s')
120
        syslog('error', 'Site list is missing: %s', mm_cfg.MAILMAN_SITE_LIST)
121
        sys.exit(1)
122
123
    # Group lists by host_name if VIRTUAL_HOST_OVERVIEW is true, otherwise
124
    # there's only one key in this dictionary: mm_cfg.DEFAULT_EMAIL_HOST.  The
125
    # values are lists of the unlocked MailList instances.
126
    byhost = {}
127
    for listname in listnames:
128
        mlist = MailList.MailList(listname, lock=0)
129
        if not mlist.send_reminders:
130
            continue
131
        if mm_cfg.VIRTUAL_HOST_OVERVIEW:
132
            host = mlist.host_name
133
        else:
134
            # See the note in Defaults.py concerning DEFAULT_HOST_NAME
135
            # vs. DEFAULT_EMAIL_HOST.
136
            host = mm_cfg.DEFAULT_HOST_NAME or mm_cfg.DEFAULT_EMAIL_HOST
137
        byhost.setdefault(host, []).append(mlist)
138
139
    # Now for each virtual host, collate the user information.  Each user
140
    # entry has the form (listaddr, password, optionsurl)
141
    for host in byhost.keys():
142
        # Site owner is `mailman@dom.ain'
143
        userinfo = {}
144
        for mlist in byhost[host]:
145
            listaddr = mlist.GetListEmail()
146
            for member in mlist.getMembers():
20 by bwarsaw
Backporting from the trunk.
147
                # The user may have disabled reminders for this list
148
                if mlist.getMemberOption(member,
149
                                         mm_cfg.SuppressPasswordReminder):
150
                    continue
6 by bwarsaw
main(): Treat addresses case-insensitively for grouping purposes.
151
                # Group by the lower-cased address, since Mailman always
152
                # treates person@dom.ain the same as PERSON@dom.ain.
81 by bwarsaw
Backporting from the HEAD -- bin and cron scripts
153
                try:
154
                    password = mlist.getMemberPassword(member)
155
                except Errors.NotAMemberError:
156
                    # Here's a member with no passwords, which I think was
157
                    # possible in older versions of Mailman.  Log this and
158
                    # move on.
159
                    syslog('error', 'password-less member %s for list %s',
160
                           member, mlist.internal_name())
161
                    continue
1 by
This commit was manufactured by cvs2svn to create branch
162
                optionsurl = mlist.GetOptionsURL(member)
163
                lang = mlist.getMemberLanguage(member)
164
                info = (listaddr, password, optionsurl, lang)
6 by bwarsaw
main(): Treat addresses case-insensitively for grouping purposes.
165
                userinfo.setdefault(member, []).append(info)
1 by
This commit was manufactured by cvs2svn to create branch
166
        # Now that we've collected user information for this host, send each
167
        # user the password reminder.
168
        for addr in userinfo.keys():
169
            # If the person is on more than one list, it is possible that they
170
            # have different preferred languages, and there's no good way to
171
            # know which one they want their password reminder in.  Pick the
172
            # most popular, and break the tie randomly.
173
            #
174
            # Also, we need an example -request address for cronpass.txt and
175
            # again, there's no clear winner.  Just take the first one in this
176
            # case.
177
            table = []
178
            langs = {}
179
            for listaddr, password, optionsurl, lang in userinfo[addr]:
180
                langs[lang] = langs.get(lang, 0) + 1
181
                # If the list address is really long, break it across two
182
                # lines.
183
                if len(listaddr) > 39:
184
                    fmt = '%s\n           %-10s\n%s\n'
185
                else:
186
                    fmt = '%-40s %-10s\n%s\n'
187
                table.append(fmt % (listaddr, password, optionsurl))
188
            # Figure out which language to use
189
            langcnt = 0
190
            poplang = None
191
            for lang, cnt in langs.items():
192
                if cnt > langcnt:
193
                    poplang = lang
194
                    langcnt = cnt
81 by bwarsaw
Backporting from the HEAD -- bin and cron scripts
195
            enc = Utils.GetCharSet(poplang)
1 by
This commit was manufactured by cvs2svn to create branch
196
            # Now we're finally ready to send the email!
197
            siteowner = Utils.get_site_email(host, 'owner')
198
            sitereq = Utils.get_site_email(host, 'request')
199
            sitebounce = Utils.get_site_email(host, 'bounces')
200
            text = Utils.maketext(
201
                'cronpass.txt',
202
                {'hostname': host,
203
                 'useraddr': addr,
204
                 'exreq'   : sitereq,
205
                 'owner'   : siteowner,
206
                 }, lang=poplang)
81 by bwarsaw
Backporting from the HEAD -- bin and cron scripts
207
            # Coerce everything to Unicode
208
            text = tounicode(text, enc)
209
            table = [tounicode(_t, enc) for _t in table]
1 by
This commit was manufactured by cvs2svn to create branch
210
            # Translate the message and headers to user's suggested lang
211
            otrans = i18n.get_translation()
212
            try:
213
                i18n.set_language(poplang)
503 by tkikuchi
i18n minor bug fix.
214
                # Craft table header after language was set
215
                header = '%-40s %-10s\n%-40s %-10s' % (
216
                         _('List'), _('Password // URL'), '----', '--------')
217
                header = tounicode(header, enc)
218
                # Add the table to the end so it doesn't get wrapped/filled
219
                text += (header + '\n' + NL.join(table))
1 by
This commit was manufactured by cvs2svn to create branch
220
                msg = Message.UserNotification(
221
                    addr, siteowner,
222
                    _('%(host)s mailing list memberships reminder'),
528 by tkikuchi
'replace' here again for alian charset password (should we restrict password
223
                    text.encode(enc, 'replace'), poplang)
503 by tkikuchi
i18n minor bug fix.
224
                # Note that text must be encoded into 'enc' because unicode
225
                # cause error within email module in some language (Japanese).
1 by
This commit was manufactured by cvs2svn to create branch
226
            finally:
227
                i18n.set_translation(otrans)
228
            msg['X-No-Archive'] = 'yes'
229
            # We want to make this look like it's coming from the siteowner's
230
            # list, but we also want to be sure that the apparent host name is
231
            # the current virtual host.  Look in CookHeaders.py for why this
232
            # trick works.  Blarg.
233
            msg.send(sitelist, **{'errorsto': sitebounce,
234
                                  '_nolist' : 1,
235
                                  'verp'    : mm_cfg.VERP_PASSWORD_REMINDERS,
236
                                  })
237
238
239

240
if __name__ == '__main__':
241
    main()