~mailman-coders/mailman/2.1

356 by bwarsaw
process_request(): Catch base class of EmailAddressError so either
1
# Copyright (C) 2001-2004 by the Free Software Foundation, Inc.
1 by
This commit was manufactured by cvs2svn to create branch
2
#
3
# This program is free software; you can redistribute it and/or
4
# modify it under the terms of the GNU General Public License
5
# as published by the Free Software Foundation; either version 2
6
# of the License, or (at your option) any later version.
128 by bwarsaw
process_request(), request_creation(): Close cross-site scripting
7
#
1 by
This commit was manufactured by cvs2svn to create branch
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
128 by bwarsaw
process_request(), request_creation(): Close cross-site scripting
12
#
1 by
This commit was manufactured by cvs2svn to create branch
13
# You should have received a copy of the GNU General Public License
128 by bwarsaw
process_request(), request_creation(): Close cross-site scripting
14
# along with this program; if not, write to the Free Software
749 by tkikuchi
FSF office has moved to 51 Franklin Street.
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1 by
This commit was manufactured by cvs2svn to create branch
16
17
"""Create mailing lists through the web."""
18
19
import sys
20
import os
21
import signal
22
import cgi
23
import sha
24
from types import ListType
25
26
from Mailman import mm_cfg
27
from Mailman import MailList
28
from Mailman import Message
29
from Mailman import Errors
30
from Mailman import i18n
31
from Mailman.htmlformat import *
32
from Mailman.Logging.Syslog import syslog
33
34
# Set up i18n
35
_ = i18n._
36
i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
37
38
39

40
def main():
41
    doc = Document()
42
    doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
43
44
    cgidata = cgi.FieldStorage()
45
    parts = Utils.GetPathPieces()
46
    if parts:
47
        # Bad URL specification
48
        title = _('Bad URL specification')
49
        doc.SetTitle(title)
50
        doc.AddItem(
51
            Header(3, Bold(FontAttr(title, color='#ff0000', size='+2'))))
52
        syslog('error', 'Bad URL specification: %s', parts)
53
    elif cgidata.has_key('doit'):
54
        # We must be processing the list creation request
55
        process_request(doc, cgidata)
56
    elif cgidata.has_key('clear'):
57
        request_creation(doc)
58
    else:
59
        # Put up the list creation request form
60
        request_creation(doc)
61
    doc.AddItem('<hr>')
62
    # Always add the footer and print the document
63
    doc.AddItem(_('Return to the ') +
64
                Link(Utils.ScriptURL('listinfo'),
65
                     _('general list overview')).Format())
66
    doc.AddItem(_('<br>Return to the ') +
67
                Link(Utils.ScriptURL('admin'),
68
                     _('administrative list overview')).Format())
69
    doc.AddItem(MailmanLogo())
70
    print doc.Format()
71
72
73

74
def process_request(doc, cgidata):
75
    # Lowercase the listname since this is treated as the "internal" name.
76
    listname = cgidata.getvalue('listname', '').strip().lower()
77
    owner    = cgidata.getvalue('owner', '').strip()
78
    try:
79
        autogen  = int(cgidata.getvalue('autogen', '0'))
80
    except ValueError:
81
        autogen = 0
82
    try:
83
        notify  = int(cgidata.getvalue('notify', '0'))
84
    except ValueError:
85
        notify = 0
86
    try:
87
        moderate = int(cgidata.getvalue('moderate', '0'))
88
    except ValueError:
89
        moderate = mm_cfg.DEFAULT_DEFAULT_MEMBER_MODERATION
128 by bwarsaw
process_request(), request_creation(): Close cross-site scripting
90
1 by
This commit was manufactured by cvs2svn to create branch
91
    password = cgidata.getvalue('password', '').strip()
92
    confirm  = cgidata.getvalue('confirm', '').strip()
93
    auth     = cgidata.getvalue('auth', '').strip()
94
    langs    = cgidata.getvalue('langs', [mm_cfg.DEFAULT_SERVER_LANGUAGE])
95
176 by bwarsaw
process_request(): In response to SF bug # 835870, we now check the
96
    if not isinstance(langs, ListType):
1 by
This commit was manufactured by cvs2svn to create branch
97
        langs = [langs]
98
    # Sanity check
128 by bwarsaw
process_request(), request_creation(): Close cross-site scripting
99
    safelistname = Utils.websafe(listname)
1 by
This commit was manufactured by cvs2svn to create branch
100
    if '@' in listname:
101
        request_creation(doc, cgidata,
128 by bwarsaw
process_request(), request_creation(): Close cross-site scripting
102
                         _('List name must not include "@": %(safelistname)s'))
1 by
This commit was manufactured by cvs2svn to create branch
103
        return
104
    if Utils.list_exists(listname):
105
        # BAW: should we tell them the list already exists?  This could be
106
        # used to mine/guess the existance of non-advertised lists.  Then
107
        # again, that can be done in other ways already, so oh well.
128 by bwarsaw
process_request(), request_creation(): Close cross-site scripting
108
        request_creation(doc, cgidata,
109
                         _('List already exists: %(safelistname)s'))
1 by
This commit was manufactured by cvs2svn to create branch
110
        return
111
    if not listname:
112
        request_creation(doc, cgidata,
113
                         _('You forgot to enter the list name'))
114
        return
115
    if not owner:
116
        request_creation(doc, cgidata,
117
                         _('You forgot to specify the list owner'))
118
        return
119
120
    if autogen:
121
        if password or confirm:
122
            request_creation(
123
                doc, cgidata,
124
                _('''Leave the initial password (and confirmation) fields
125
                blank if you want Mailman to autogenerate the list
126
                passwords.'''))
127
            return
567 by bwarsaw
From the NEWS file:
128
        password = confirm = Utils.MakeRandomPassword(
129
            mm_cfg.ADMIN_PASSWORD_LENGTH)
1 by
This commit was manufactured by cvs2svn to create branch
130
    else:
131
        if password <> confirm:
132
            request_creation(doc, cgidata,
133
                             _('Initial list passwords do not match'))
134
            return
135
        if not password:
136
            request_creation(
137
                doc, cgidata,
138
                # The little <!-- ignore --> tag is used so that this string
139
                # differs from the one in bin/newlist.  The former is destined
140
                # for the web while the latter is destined for email, so they
141
                # must be different entries in the message catalog.
142
                _('The list password cannot be empty<!-- ignore -->'))
143
            return
144
    # The authorization password must be non-empty, and it must match either
145
    # the list creation password or the site admin password
146
    ok = 0
147
    if auth:
148
        ok = Utils.check_global_password(auth, 0)
149
        if not ok:
150
            ok = Utils.check_global_password(auth)
151
    if not ok:
152
        request_creation(
153
            doc, cgidata,
154
            _('You are not authorized to create new mailing lists'))
155
        return
176 by bwarsaw
process_request(): In response to SF bug # 835870, we now check the
156
    # Make sure the web hostname matches one of our virtual domains
157
    hostname = Utils.get_domain()
158
    if mm_cfg.VIRTUAL_HOST_OVERVIEW and \
159
           not mm_cfg.VIRTUAL_HOSTS.has_key(hostname):
160
        safehostname = Utils.websafe(hostname)
161
        request_creation(doc, cgidata,
162
                         _('Unknown virtual host: %(safehostname)s'))
163
        return
164
    emailhost = mm_cfg.VIRTUAL_HOSTS.get(hostname, mm_cfg.DEFAULT_EMAIL_HOST)
1 by
This commit was manufactured by cvs2svn to create branch
165
    # We've got all the data we need, so go ahead and try to create the list
166
    # See admin.py for why we need to set up the signal handler.
167
    mlist = MailList.MailList()
168
169
    def sigterm_handler(signum, frame, mlist=mlist):
170
        # Make sure the list gets unlocked...
171
        mlist.Unlock()
172
        # ...and ensure we exit, otherwise race conditions could cause us to
173
        # enter MailList.Save() while we're in the unlocked state, and that
174
        # could be bad!
175
        sys.exit(0)
176
177
    try:
178
        # Install the emergency shutdown signal handler
179
        signal.signal(signal.SIGTERM, sigterm_handler)
180
181
        pw = sha.new(password).hexdigest()
182
        # Guarantee that all newly created files have the proper permission.
183
        # proper group ownership should be assured by the autoconf script
184
        # enforcing that all directories have the group sticky bit set
185
        oldmask = os.umask(002)
186
        try:
187
            try:
176 by bwarsaw
process_request(): In response to SF bug # 835870, we now check the
188
                mlist.Create(listname, owner, pw, langs, emailhost)
1 by
This commit was manufactured by cvs2svn to create branch
189
            finally:
190
                os.umask(oldmask)
356 by bwarsaw
process_request(): Catch base class of EmailAddressError so either
191
        except Errors.EmailAddressError, s:
1 by
This commit was manufactured by cvs2svn to create branch
192
            request_creation(doc, cgidata,
193
                             _('Bad owner email address: %(s)s'))
194
            return
195
        except Errors.MMListAlreadyExistsError:
196
            request_creation(doc, cgidata,
197
                             _('List already exists: %(listname)s'))
198
            return
199
        except Errors.BadListNameError, s:
200
            request_creation(doc, cgidata,
201
                             _('Illegal list name: %(s)s'))
202
            return
203
        except Errors.MMListError:
204
            request_creation(
205
                doc, cgidata,
206
                _('''Some unknown error occurred while creating the list.
207
                Please contact the site administrator for assistance.'''))
208
            return
128 by bwarsaw
process_request(), request_creation(): Close cross-site scripting
209
1 by
This commit was manufactured by cvs2svn to create branch
210
        # Initialize the host_name and web_page_url attributes, based on
211
        # virtual hosting settings and the request environment variables.
212
        mlist.default_member_moderation = moderate
213
        mlist.web_page_url = mm_cfg.DEFAULT_URL_PATTERN % hostname
176 by bwarsaw
process_request(): In response to SF bug # 835870, we now check the
214
        mlist.host_name = emailhost
1 by
This commit was manufactured by cvs2svn to create branch
215
        mlist.Save()
216
    finally:
217
        # Now be sure to unlock the list.  It's okay if we get a signal here
218
        # because essentially, the signal handler will do the same thing.  And
219
        # unlocking is unconditional, so it's not an error if we unlock while
220
        # we're already unlocked.
221
        mlist.Unlock()
222
223
    # Now do the MTA-specific list creation tasks
224
    if mm_cfg.MTA:
225
        modname = 'Mailman.MTA.' + mm_cfg.MTA
226
        __import__(modname)
227
        sys.modules[modname].create(mlist, cgi=1)
228
229
    # And send the notice to the list owner.
230
    if notify:
488 by tkikuchi
[ 874764 ] -admin address is now equiv to -bounce
231
        siteowner = Utils.get_site_email(mlist.host_name, 'owner')
1 by
This commit was manufactured by cvs2svn to create branch
232
        text = Utils.maketext(
233
            'newlist.txt',
234
            {'listname'    : listname,
128 by bwarsaw
process_request(), request_creation(): Close cross-site scripting
235
             'password'    : password,
236
             'admin_url'   : mlist.GetScriptURL('admin', absolute=1),
1 by
This commit was manufactured by cvs2svn to create branch
237
             'listinfo_url': mlist.GetScriptURL('listinfo', absolute=1),
238
             'requestaddr' : mlist.GetRequestEmail(),
488 by tkikuchi
[ 874764 ] -admin address is now equiv to -bounce
239
             'siteowner'   : siteowner,
1 by
This commit was manufactured by cvs2svn to create branch
240
             }, mlist=mlist)
241
        msg = Message.UserNotification(
488 by tkikuchi
[ 874764 ] -admin address is now equiv to -bounce
242
            owner, siteowner,
1 by
This commit was manufactured by cvs2svn to create branch
243
            _('Your new mailing list: %(listname)s'),
244
            text, mlist.preferred_language)
245
        msg.send(mlist)
246
247
    # Success!
248
    listinfo_url = mlist.GetScriptURL('listinfo', absolute=1)
249
    admin_url = mlist.GetScriptURL('admin', absolute=1)
250
    create_url = Utils.ScriptURL('create')
251
252
    title = _('Mailing list creation results')
253
    doc.SetTitle(title)
254
    table = Table(border=0, width='100%')
255
    table.AddRow([Center(Bold(FontAttr(title, size='+1')))])
256
    table.AddCellInfo(table.GetCurrentRowIndex(), 0,
257
                      bgcolor=mm_cfg.WEB_HEADER_COLOR)
258
    table.AddRow([_('''You have successfully created the mailing list
259
    <b>%(listname)s</b> and notification has been sent to the list owner
260
    <b>%(owner)s</b>.  You can now:''')])
261
    ullist = UnorderedList()
262
    ullist.AddItem(Link(listinfo_url, _("Visit the list's info page")))
263
    ullist.AddItem(Link(admin_url, _("Visit the list's admin page")))
264
    ullist.AddItem(Link(create_url, _('Create another list')))
265
    table.AddRow([ullist])
266
    doc.AddItem(table)
267
268
269

270
# Because the cgi module blows
271
class Dummy:
272
    def getvalue(self, name, default):
273
        return default
274
dummy = Dummy()
275
276
277

278
def request_creation(doc, cgidata=dummy, errmsg=None):
279
    # What virtual domain are we using?
280
    hostname = Utils.get_domain()
281
    # Set up the document
282
    title = _('Create a %(hostname)s Mailing List')
283
    doc.SetTitle(title)
284
    table = Table(border=0, width='100%')
285
    table.AddRow([Center(Bold(FontAttr(title, size='+1')))])
286
    table.AddCellInfo(table.GetCurrentRowIndex(), 0,
287
                      bgcolor=mm_cfg.WEB_HEADER_COLOR)
288
    # Add any error message
289
    if errmsg:
290
        table.AddRow([Header(3, Bold(
291
            FontAttr(_('Error: '), color='#ff0000', size='+2').Format() +
292
            Italic(errmsg).Format()))])
293
    table.AddRow([_("""You can create a new mailing list by entering the
294
    relevant information into the form below.  The name of the mailing list
295
    will be used as the primary address for posting messages to the list, so
296
    it should be lowercased.  You will not be able to change this once the
297
    list is created.
298
299
    <p>You also need to enter the email address of the initial list owner.
300
    Once the list is created, the list owner will be given notification, along
301
    with the initial list password.  The list owner will then be able to
302
    modify the password and add or remove additional list owners.
303
304
    <p>If you want Mailman to automatically generate the initial list admin
305
    password, click on `Yes' in the autogenerate field below, and leave the
306
    initial list password fields empty.
307
308
    <p>You must have the proper authorization to create new mailing lists.
309
    Each site should have a <em>list creator's</em> password, which you can
310
    enter in the field at the bottom.  Note that the site administrator's
311
    password can also be used for authentication.
312
    """)])
313
    # Build the form for the necessary input
314
    GREY = mm_cfg.WEB_ADMINITEM_COLOR
315
    form = Form(Utils.ScriptURL('create'))
316
    ftable = Table(border=0, cols='2', width='100%',
317
                   cellspacing=3, cellpadding=4)
318
319
    ftable.AddRow([Center(Italic(_('List Identity')))])
320
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, colspan=2)
321
128 by bwarsaw
process_request(), request_creation(): Close cross-site scripting
322
    safelistname = Utils.websafe(cgidata.getvalue('listname', ''))
1 by
This commit was manufactured by cvs2svn to create branch
323
    ftable.AddRow([Label(_('Name of list:')),
128 by bwarsaw
process_request(), request_creation(): Close cross-site scripting
324
                   TextBox('listname', safelistname)])
1 by
This commit was manufactured by cvs2svn to create branch
325
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
326
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
327
128 by bwarsaw
process_request(), request_creation(): Close cross-site scripting
328
    safeowner = Utils.websafe(cgidata.getvalue('owner', ''))
1 by
This commit was manufactured by cvs2svn to create branch
329
    ftable.AddRow([Label(_('Initial list owner address:')),
128 by bwarsaw
process_request(), request_creation(): Close cross-site scripting
330
                   TextBox('owner', safeowner)])
1 by
This commit was manufactured by cvs2svn to create branch
331
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
332
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
333
334
    try:
335
        autogen = int(cgidata.getvalue('autogen', '0'))
336
    except ValueError:
337
        autogen = 0
338
    ftable.AddRow([Label(_('Auto-generate initial list password?')),
339
                   RadioButtonArray('autogen', (_('No'), _('Yes')),
340
                                    checked=autogen,
341
                                    values=(0, 1))])
342
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
343
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
344
128 by bwarsaw
process_request(), request_creation(): Close cross-site scripting
345
    safepasswd = Utils.websafe(cgidata.getvalue('password', ''))
1 by
This commit was manufactured by cvs2svn to create branch
346
    ftable.AddRow([Label(_('Initial list password:')),
128 by bwarsaw
process_request(), request_creation(): Close cross-site scripting
347
                   PasswordBox('password', safepasswd)])
1 by
This commit was manufactured by cvs2svn to create branch
348
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
349
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
350
128 by bwarsaw
process_request(), request_creation(): Close cross-site scripting
351
    safeconfirm = Utils.websafe(cgidata.getvalue('confirm', ''))
1 by
This commit was manufactured by cvs2svn to create branch
352
    ftable.AddRow([Label(_('Confirm initial password:')),
128 by bwarsaw
process_request(), request_creation(): Close cross-site scripting
353
                   PasswordBox('confirm', safeconfirm)])
1 by
This commit was manufactured by cvs2svn to create branch
354
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
355
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
356
357
    try:
358
        notify = int(cgidata.getvalue('notify', '1'))
359
    except ValueError:
360
        notify = 1
361
362
    ftable.AddRow([Center(Italic(_('List Characteristics')))])
363
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, colspan=2)
364
365
    ftable.AddRow([
366
        Label(_("""Should new members be quarantined before they
367
    are allowed to post unmoderated to this list?  Answer <em>Yes</em> to hold
368
    new member postings for moderator approval by default.""")),
369
        RadioButtonArray('moderate', (_('No'), _('Yes')),
370
                         checked=mm_cfg.DEFAULT_DEFAULT_MEMBER_MODERATION,
371
                         values=(0,1))])
372
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
373
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
374
    # Create the table of initially supported languages, sorted on the long
375
    # name of the language.
376
    revmap = {}
377
    for key, (name, charset) in mm_cfg.LC_DESCRIPTIONS.items():
378
        revmap[_(name)] = key
379
    langnames = revmap.keys()
380
    langnames.sort()
381
    langs = []
382
    for name in langnames:
383
        langs.append(revmap[name])
384
    try:
385
        langi = langs.index(mm_cfg.DEFAULT_SERVER_LANGUAGE)
386
    except ValueError:
387
        # Someone must have deleted the servers's preferred language.  Could
388
        # be other trouble lurking!
389
        langi = 0
390
    # BAW: we should preserve the list of checked languages across form
391
    # invocations.
392
    checked = [0] * len(langs)
393
    checked[langi] = 1
394
    deflang = _(Utils.GetLanguageDescr(mm_cfg.DEFAULT_SERVER_LANGUAGE))
395
    ftable.AddRow([Label(_(
396
        '''Initial list of supported languages.  <p>Note that if you do not
397
        select at least one initial language, the list will use the server
398
        default language of %(deflang)s''')),
399
                   CheckBoxArray('langs',
400
                                 [_(Utils.GetLanguageDescr(L)) for L in langs],
401
                                 checked=checked,
402
                                 values=langs)])
403
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
404
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
405
406
    ftable.AddRow([Label(_('Send "list created" email to list owner?')),
407
                   RadioButtonArray('notify', (_('No'), _('Yes')),
408
                                    checked=notify,
409
                                    values=(0, 1))])
410
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
411
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
412
413
    ftable.AddRow(['<hr>'])
414
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, colspan=2)
415
    ftable.AddRow([Label(_("List creator's (authentication) password:")),
416
                   PasswordBox('auth')])
417
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
418
    ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
419
420
    ftable.AddRow([Center(SubmitButton('doit', _('Create List'))),
421
                   Center(SubmitButton('clear', _('Clear Form')))])
422
    form.AddItem(ftable)
423
    table.AddRow([form])
424
    doc.AddItem(table)