summaryrefslogtreecommitdiff
path: root/mailman/web/Cgi
diff options
context:
space:
mode:
Diffstat (limited to 'mailman/web/Cgi')
-rw-r--r--mailman/web/Cgi/Auth.py60
-rw-r--r--mailman/web/Cgi/__init__.py0
-rw-r--r--mailman/web/Cgi/admin.py1433
-rw-r--r--mailman/web/Cgi/admindb.py813
-rw-r--r--mailman/web/Cgi/confirm.py834
-rw-r--r--mailman/web/Cgi/create.py400
-rw-r--r--mailman/web/Cgi/edithtml.py175
-rw-r--r--mailman/web/Cgi/listinfo.py207
-rw-r--r--mailman/web/Cgi/options.py1000
-rw-r--r--mailman/web/Cgi/private.py190
-rw-r--r--mailman/web/Cgi/rmlist.py243
-rw-r--r--mailman/web/Cgi/roster.py130
-rw-r--r--mailman/web/Cgi/subscribe.py252
-rw-r--r--mailman/web/Cgi/wsgi_app.py286
14 files changed, 0 insertions, 6023 deletions
diff --git a/mailman/web/Cgi/Auth.py b/mailman/web/Cgi/Auth.py
deleted file mode 100644
index 825d972f4..000000000
--- a/mailman/web/Cgi/Auth.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# Copyright (C) 1998-2009 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""Common routines for logging in and logging out of the list administrator
-and list moderator interface.
-"""
-
-from Mailman import Utils
-from Mailman.htmlformat import FontAttr
-from Mailman.i18n import _
-
-
-
-class NotLoggedInError(Exception):
- """Exception raised when no matching admin cookie was found."""
- def __init__(self, message):
- Exception.__init__(self, message)
- self.message = message
-
-
-
-def loginpage(mlist, scriptname, msg='', frontpage=False):
- url = mlist.GetScriptURL(scriptname)
- if frontpage:
- actionurl = url
- else:
- request = Utils.GetRequestURI(url).lstrip('/')
- up = '../' * request.count('/')
- actionurl = up + request
- if msg:
- msg = FontAttr(msg, color='#ff0000', size='+1').Format()
- if scriptname == 'admindb':
- who = _('Moderator')
- else:
- who = _('Administrator')
- # Language stuff
- charset = Utils.GetCharSet(mlist.preferred_language)
- print 'Content-type: text/html; charset=' + charset + '\n\n'
- print Utils.maketext(
- 'admlogin.html',
- {'listname': mlist.real_name,
- 'path' : actionurl,
- 'message' : msg,
- 'who' : who,
- }, mlist=mlist).encode(charset)
- print mlist.GetMailmanFooter().encode(charset)
diff --git a/mailman/web/Cgi/__init__.py b/mailman/web/Cgi/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/mailman/web/Cgi/__init__.py
+++ /dev/null
diff --git a/mailman/web/Cgi/admin.py b/mailman/web/Cgi/admin.py
deleted file mode 100644
index e5c6ee14b..000000000
--- a/mailman/web/Cgi/admin.py
+++ /dev/null
@@ -1,1433 +0,0 @@
-# Copyright (C) 1998-2009 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""Process and produce the list-administration options forms."""
-
-import os
-import re
-import cgi
-import sha
-import sys
-import urllib
-import logging
-
-from email.Utils import unquote, parseaddr, formataddr
-from string import lowercase, digits
-
-from Mailman import Errors
-from Mailman import MailList
-from Mailman import MemberAdaptor
-from Mailman import Utils
-from Mailman import i18n
-from Mailman import passwords
-from Mailman.Cgi import Auth
-from Mailman.UserDesc import UserDesc
-from Mailman.configuration import config
-from Mailman.htmlformat import *
-
-# Set up i18n
-_ = i18n._
-i18n.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
-NL = '\n'
-OPTCOLUMNS = 11
-
-log = logging.getLogger('mailman.error')
-
-
-
-def main():
- # Try to find out which list is being administered
- parts = Utils.GetPathPieces()
- if not parts:
- # None, so just do the admin overview and be done with it
- admin_overview()
- return
- # Get the list object
- listname = parts[0].lower()
- try:
- mlist = MailList.MailList(listname, lock=False)
- except Errors.MMListError, e:
- # Avoid cross-site scripting attacks
- safelistname = Utils.websafe(listname)
- admin_overview(_('No such list <em>%(safelistname)s</em>'))
- log.error('admin.py access for non-existent list: %s', listname)
- return
- # Now that we know what list has been requested, all subsequent admin
- # pages are shown in that list's preferred language.
- i18n.set_language(mlist.preferred_language)
- # If the user is not authenticated, we're done.
- cgidata = cgi.FieldStorage(keep_blank_values=1)
-
- if not mlist.WebAuthenticate((config.AuthListAdmin,
- config.AuthSiteAdmin),
- cgidata.getvalue('adminpw', '')):
- if cgidata.has_key('adminpw'):
- # This is a re-authorization attempt
- msg = Bold(FontSize('+1', _('Authorization failed.'))).Format()
- else:
- msg = ''
- Auth.loginpage(mlist, 'admin', msg=msg)
- return
-
- # Which subcategory was requested? Default is `general'
- if len(parts) == 1:
- category = 'general'
- subcat = None
- elif len(parts) == 2:
- category = parts[1]
- subcat = None
- else:
- category = parts[1]
- subcat = parts[2]
-
- # Is this a log-out request?
- if category == 'logout':
- print mlist.ZapCookie(config.AuthListAdmin)
- Auth.loginpage(mlist, 'admin', frontpage=True)
- return
-
- # Sanity check
- if category not in mlist.GetConfigCategories().keys():
- category = 'general'
-
- # Is the request for variable details?
- varhelp = None
- qsenviron = os.environ.get('QUERY_STRING')
- parsedqs = None
- if qsenviron:
- parsedqs = cgi.parse_qs(qsenviron)
- if cgidata.has_key('VARHELP'):
- varhelp = cgidata.getvalue('VARHELP')
- elif parsedqs:
- # POST methods, even if their actions have a query string, don't get
- # put into FieldStorage's keys :-(
- qs = parsedqs.get('VARHELP')
- if qs and isinstance(qs, list):
- varhelp = qs[0]
- if varhelp:
- option_help(mlist, varhelp)
- return
-
- # The html page document
- doc = Document()
- doc.set_language(mlist.preferred_language)
- mlist.Lock()
- try:
- if cgidata.keys():
- # There are options to change
- change_options(mlist, category, subcat, cgidata, doc)
- # Let the list sanity check the changed values
- mlist.CheckValues()
- # Additional sanity checks
- if not mlist.digestable and not mlist.nondigestable:
- doc.addError(
- _('''You have turned off delivery of both digest and
- non-digest messages. This is an incompatible state of
- affairs. You must turn on either digest delivery or
- non-digest delivery or your mailing list will basically be
- unusable.'''), tag=_('Warning: '))
-
- if not mlist.digestable and mlist.getDigestMemberKeys():
- doc.addError(
- _('''You have digest members, but digests are turned
- off. Those people will not receive mail.'''),
- tag=_('Warning: '))
- if not mlist.nondigestable and mlist.getRegularMemberKeys():
- doc.addError(
- _('''You have regular list members but non-digestified mail is
- turned off. They will receive mail until you fix this
- problem.'''), tag=_('Warning: '))
- # Glom up the results page and print it out
- show_results(mlist, doc, category, subcat, cgidata)
- print doc.Format()
- mlist.Save()
- finally:
- mlist.Unlock()
-
-
-
-def admin_overview(msg=''):
- # Show the administrative overview page, with the list of all the lists on
- # this host. msg is an optional error message to display at the top of
- # the page.
- #
- # This page should be displayed in the server's default language, which
- # should have already been set.
- hostname = Utils.get_request_domain()
- legend = _('%(hostname)s mailing lists - Admin Links')
- # The html `document'
- doc = Document()
- doc.set_language(config.DEFAULT_SERVER_LANGUAGE)
- doc.SetTitle(legend)
- # The table that will hold everything
- table = Table(border=0, width="100%")
- table.AddRow([Center(Header(2, legend))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2,
- bgcolor=config.WEB_HEADER_COLOR)
- # Skip any mailing list that isn't advertised.
- advertised = []
- for name in sorted(config.list_manager.names):
- mlist = MailList.MailList(name, lock=False)
- if mlist.advertised:
- if hostname not in mlist.web_page_url:
- # This list is situated in a different virtual domain
- continue
- else:
- advertised.append((mlist.GetScriptURL('admin'),
- mlist.real_name,
- mlist.description))
- # Greeting depends on whether there was an error or not
- if msg:
- greeting = FontAttr(msg, color="ff5060", size="+1")
- else:
- greeting = _("Welcome!")
-
- welcome = []
- mailmanlink = Link(config.MAILMAN_URL, _('Mailman')).Format()
- if not advertised:
- welcome.extend([
- greeting,
- _('''<p>There currently are no publicly-advertised %(mailmanlink)s
- mailing lists on %(hostname)s.'''),
- ])
- else:
- welcome.extend([
- greeting,
- _('''<p>Below is the collection of publicly-advertised
- %(mailmanlink)s mailing lists on %(hostname)s. Click on a list
- name to visit the configuration pages for that list.'''),
- ])
-
- creatorurl = Utils.ScriptURL('create')
- mailman_owner = Utils.get_site_noreply()
- extra = msg and _('right ') or ''
- welcome.extend([
- _('''To visit the administrators configuration page for an
- unadvertised list, open a URL similar to this one, but with a '/' and
- the %(extra)slist name appended. If you have the proper authority,
- you can also <a href="%(creatorurl)s">create a new mailing list</a>.
-
- <p>General list information can be found at '''),
- Link(Utils.ScriptURL('listinfo'),
- _('the mailing list overview page')),
- '.',
- _('<p>(Send questions and comments to '),
- Link('mailto:%s' % mailman_owner, mailman_owner),
- '.)<p>',
- ])
-
- table.AddRow([Container(*welcome)])
- table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 0, colspan=2)
-
- if advertised:
- table.AddRow(['&nbsp;', '&nbsp;'])
- table.AddRow([Bold(FontAttr(_('List'), size='+2')),
- Bold(FontAttr(_('Description'), size='+2'))
- ])
- highlight = 1
- for url, real_name, description in advertised:
- table.AddRow(
- [Link(url, Bold(real_name)),
- description or Italic(_('[no description available]'))])
- if highlight and config.WEB_HIGHLIGHT_COLOR:
- table.AddRowInfo(table.GetCurrentRowIndex(),
- bgcolor=config.WEB_HIGHLIGHT_COLOR)
- highlight = not highlight
-
- doc.AddItem(table)
- doc.AddItem('<hr>')
- doc.AddItem(MailmanLogo())
- print doc.Format()
-
-
-
-def option_help(mlist, varhelp):
- # The html page document
- doc = Document()
- doc.set_language(mlist.preferred_language)
- # Find out which category and variable help is being requested for.
- item = None
- reflist = varhelp.split('/')
- if len(reflist) >= 2:
- category = subcat = None
- if len(reflist) == 2:
- category, varname = reflist
- elif len(reflist) == 3:
- category, subcat, varname = reflist
- options = mlist.GetConfigInfo(category, subcat)
- if options:
- for i in options:
- if i and i[0] == varname:
- item = i
- break
- # Print an error message if we couldn't find a valid one
- if not item:
- bad = _('No valid variable name found.')
- doc.addError(bad)
- doc.AddItem(mlist.GetMailmanFooter())
- print doc.Format()
- return
- # Get the details about the variable
- varname, kind, params, dependancies, description, elaboration = \
- get_item_characteristics(item)
- # Set up the document
- realname = mlist.real_name
- legend = _("""%(realname)s Mailing list Configuration Help
- <br><em>%(varname)s</em> Option""")
-
- header = Table(width='100%')
- header.AddRow([Center(Header(3, legend))])
- header.AddCellInfo(header.GetCurrentRowIndex(), 0, colspan=2,
- bgcolor=config.WEB_HEADER_COLOR)
- doc.SetTitle(_("Mailman %(varname)s List Option Help"))
- doc.AddItem(header)
- doc.AddItem("<b>%s</b> (%s): %s<p>" % (varname, category, description))
- if elaboration:
- doc.AddItem("%s<p>" % elaboration)
-
- if subcat:
- url = '%s/%s/%s' % (mlist.GetScriptURL('admin'), category, subcat)
- else:
- url = '%s/%s' % (mlist.GetScriptURL('admin'), category)
- form = Form(url)
- valtab = Table(cellspacing=3, cellpadding=4, width='100%')
- add_options_table_item(mlist, category, subcat, valtab, item, detailsp=0)
- form.AddItem(valtab)
- form.AddItem('<p>')
- form.AddItem(Center(submit_button()))
- doc.AddItem(Center(form))
-
- doc.AddItem(_("""<em><strong>Warning:</strong> changing this option here
- could cause other screens to be out-of-sync. Be sure to reload any other
- pages that are displaying this option for this mailing list. You can also
- """))
-
- adminurl = mlist.GetScriptURL('admin')
- if subcat:
- url = '%s/%s/%s' % (adminurl, category, subcat)
- else:
- url = '%s/%s' % (adminurl, category)
- categoryname = mlist.GetConfigCategories()[category][0]
- doc.AddItem(Link(url, _('return to the %(categoryname)s options page.')))
- doc.AddItem('</em>')
- doc.AddItem(mlist.GetMailmanFooter())
- print doc.Format()
-
-
-
-def show_results(mlist, doc, category, subcat, cgidata):
- # Produce the results page
- adminurl = mlist.GetScriptURL('admin')
- categories = mlist.GetConfigCategories()
- label = _(categories[category][0])
-
- # Set up the document's headers
- realname = mlist.real_name
- doc.SetTitle(_('%(realname)s Administration (%(label)s)'))
- doc.AddItem(Center(Header(2, _(
- '%(realname)s mailing list administration<br>%(label)s Section'))))
- doc.AddItem('<hr>')
- # Now we need to craft the form that will be submitted, which will contain
- # all the variable settings, etc. This is a bit of a kludge because we
- # know that the autoreply and members categories supports file uploads.
- encoding = None
- if category in ('autoreply', 'members'):
- encoding = 'multipart/form-data'
- if subcat:
- form = Form('%s/%s/%s' % (adminurl, category, subcat),
- encoding=encoding)
- else:
- form = Form('%s/%s' % (adminurl, category), encoding=encoding)
- # This holds the two columns of links
- linktable = Table(valign='top', width='100%')
- linktable.AddRow([Center(Bold(_("Configuration Categories"))),
- Center(Bold(_("Other Administrative Activities")))])
- # The `other links' are stuff in the right column.
- otherlinks = UnorderedList()
- otherlinks.AddItem(Link(mlist.GetScriptURL('admindb'),
- _('Tend to pending moderator requests')))
- otherlinks.AddItem(Link(mlist.GetScriptURL('listinfo'),
- _('Go to the general list information page')))
- otherlinks.AddItem(Link(mlist.GetScriptURL('edithtml'),
- _('Edit the public HTML pages and text files')))
- otherlinks.AddItem(Link(mlist.GetBaseArchiveURL(),
- _('Go to list archives')).Format() +
- '<br>&nbsp;<br>')
- if config.OWNERS_CAN_DELETE_THEIR_OWN_LISTS:
- otherlinks.AddItem(Link(mlist.GetScriptURL('rmlist'),
- _('Delete this mailing list')).Format() +
- _(' (requires confirmation)<br>&nbsp;<br>'))
- otherlinks.AddItem(Link('%s/logout' % adminurl,
- # BAW: What I really want is a blank line, but
- # adding an &nbsp; won't do it because of the
- # bullet added to the list item.
- '<FONT SIZE="+2"><b>%s</b></FONT>' %
- _('Logout')))
- # These are links to other categories and live in the left column
- categorylinks_1 = categorylinks = UnorderedList()
- categorylinks_2 = ''
- categorykeys = categories.keys()
- half = len(categorykeys) / 2
- counter = 0
- subcat = None
- for k in categorykeys:
- label = _(categories[k][0])
- url = '%s/%s' % (adminurl, k)
- if k == category:
- # Handle subcategories
- subcats = mlist.GetConfigSubCategories(k)
- if subcats:
- subcat = Utils.GetPathPieces()[-1]
- for k, v in subcats:
- if k == subcat:
- break
- else:
- # The first subcategory in the list is the default
- subcat = subcats[0][0]
- subcat_items = []
- for sub, text in subcats:
- if sub == subcat:
- text = Bold('[%s]' % text).Format()
- subcat_items.append(Link(url + '/' + sub, text))
- categorylinks.AddItem(
- Bold(label).Format() +
- UnorderedList(*subcat_items).Format())
- else:
- categorylinks.AddItem(Link(url, Bold('[%s]' % label)))
- else:
- categorylinks.AddItem(Link(url, label))
- counter += 1
- if counter >= half:
- categorylinks_2 = categorylinks = UnorderedList()
- counter = -len(categorykeys)
- # Make the emergency stop switch a rude solo light
- etable = Table()
- # Add all the links to the links table...
- etable.AddRow([categorylinks_1, categorylinks_2])
- etable.AddRowInfo(etable.GetCurrentRowIndex(), valign='top')
- if mlist.emergency:
- label = _('Emergency moderation of all list traffic is enabled')
- etable.AddRow([Center(
- Link('?VARHELP=general/emergency', Bold(label)))])
- color = config.WEB_ERROR_COLOR
- etable.AddCellInfo(etable.GetCurrentRowIndex(), 0,
- colspan=2, bgcolor=color)
- linktable.AddRow([etable, otherlinks])
- # ...and add the links table to the document.
- form.AddItem(linktable)
- form.AddItem('<hr>')
- form.AddItem(
- _('''Make your changes in the following section, then submit them
- using the <em>Submit Your Changes</em> button below.''')
- + '<p>')
-
- # The members and passwords categories are special in that they aren't
- # defined in terms of gui elements. Create those pages here.
- if category == 'members':
- # Figure out which subcategory we should display
- subcat = Utils.GetPathPieces()[-1]
- if subcat not in ('list', 'add', 'remove'):
- subcat = 'list'
- # Add member category specific tables
- form.AddItem(membership_options(mlist, subcat, cgidata, doc, form))
- form.AddItem(Center(submit_button('setmemberopts_btn')))
- # In "list" subcategory, we can also search for members
- if subcat == 'list':
- form.AddItem('<hr>\n')
- table = Table(width='100%')
- table.AddRow([Center(Header(2, _('Additional Member Tasks')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2,
- bgcolor=config.WEB_HEADER_COLOR)
- # Add a blank separator row
- table.AddRow(['&nbsp;', '&nbsp;'])
- # Add a section to set the moderation bit for all members
- table.AddRow([_("""<li>Set everyone's moderation bit, including
- those members not currently visible""")])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- table.AddRow([RadioButtonArray('allmodbit_val',
- (_('Off'), _('On')),
- mlist.default_member_moderation),
- SubmitButton('allmodbit_btn', _('Set'))])
- form.AddItem(table)
- elif category == 'passwords':
- form.AddItem(Center(password_inputs(mlist)))
- form.AddItem(Center(submit_button()))
- else:
- form.AddItem(show_variables(mlist, category, subcat, cgidata, doc))
- form.AddItem(Center(submit_button()))
- # And add the form
- doc.AddItem(form)
- doc.AddItem(mlist.GetMailmanFooter())
-
-
-
-def show_variables(mlist, category, subcat, cgidata, doc):
- options = mlist.GetConfigInfo(category, subcat)
-
- # The table containing the results
- table = Table(cellspacing=3, cellpadding=4, width='100%')
-
- # Get and portray the text label for the category.
- categories = mlist.GetConfigCategories()
- label = _(categories[category][0])
-
- table.AddRow([Center(Header(2, label))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2,
- bgcolor=config.WEB_HEADER_COLOR)
-
- # The very first item in the config info will be treated as a general
- # description if it is a string
- description = options[0]
- if isinstance(description, basestring):
- table.AddRow([description])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- options = options[1:]
-
- if not options:
- return table
-
- # Add the global column headers
- table.AddRow([Center(Bold(_('Description'))),
- Center(Bold(_('Value')))])
- table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 0,
- width='15%')
- table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 1,
- width='85%')
-
- for item in options:
- if isinstance(item, basestring):
- # The very first banner option (string in an options list) is
- # treated as a general description, while any others are
- # treated as section headers - centered and italicized...
- table.AddRow([Center(Italic(item))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- else:
- add_options_table_item(mlist, category, subcat, table, item)
- table.AddRow(['<br>'])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- return table
-
-
-
-def add_options_table_item(mlist, category, subcat, table, item, detailsp=1):
- # Add a row to an options table with the item description and value.
- varname, kind, params, extra, descr, elaboration = \
- get_item_characteristics(item)
- if elaboration is None:
- elaboration = descr
- descr = get_item_gui_description(mlist, category, subcat,
- varname, descr, elaboration, detailsp)
- val = get_item_gui_value(mlist, category, kind, varname, params, extra)
- table.AddRow([descr, val])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0,
- bgcolor=config.WEB_ADMINITEM_COLOR)
- table.AddCellInfo(table.GetCurrentRowIndex(), 1,
- bgcolor=config.WEB_ADMINITEM_COLOR)
-
-
-
-def get_item_characteristics(record):
- # Break out the components of an item description from its description
- # record:
- #
- # 0 -- option-var name
- # 1 -- type
- # 2 -- entry size
- # 3 -- ?dependancies?
- # 4 -- Brief description
- # 5 -- Optional description elaboration
- if len(record) == 5:
- elaboration = None
- varname, kind, params, dependancies, descr = record
- elif len(record) == 6:
- varname, kind, params, dependancies, descr, elaboration = record
- else:
- raise ValueError, _('Badly formed options entry:\n %(record)s')
- return varname, kind, params, dependancies, descr, elaboration
-
-
-
-def get_item_gui_value(mlist, category, kind, varname, params, extra):
- """Return a representation of an item's settings."""
- # Give the category a chance to return the value for the variable
- value = None
- label, gui = mlist.GetConfigCategories()[category]
- if hasattr(gui, 'getValue'):
- value = gui.getValue(mlist, kind, varname, params)
- # Filter out None, and volatile attributes
- if value is None and not varname.startswith('_'):
- value = getattr(mlist, varname)
- # Now create the widget for this value
- if kind == config.Radio or kind == config.Toggle:
- # If we are returning the option for subscribe policy and this site
- # doesn't allow open subscribes, then we have to alter the value of
- # mlist.subscribe_policy as passed to RadioButtonArray in order to
- # compensate for the fact that there is one fewer option.
- # Correspondingly, we alter the value back in the change options
- # function -scott
- #
- # TBD: this is an ugly ugly hack.
- if varname.startswith('_'):
- checked = 0
- else:
- checked = value
- if varname == 'subscribe_policy' and not config.ALLOW_OPEN_SUBSCRIBE:
- checked = checked - 1
- # For Radio buttons, we're going to interpret the extra stuff as a
- # horizontal/vertical flag. For backwards compatibility, the value 0
- # means horizontal, so we use "not extra" to get the parity right.
- return RadioButtonArray(varname, params, checked, not extra)
- elif (kind == config.String or kind == config.Email or
- kind == config.Host or kind == config.Number):
- return TextBox(varname, value, params)
- elif kind == config.Text:
- if params:
- r, c = params
- else:
- r, c = None, None
- return TextArea(varname, value or '', r, c)
- elif kind in (config.EmailList, config.EmailListEx):
- if params:
- r, c = params
- else:
- r, c = None, None
- res = NL.join(value)
- return TextArea(varname, res, r, c, wrap='off')
- elif kind == config.FileUpload:
- # like a text area, but also with uploading
- if params:
- r, c = params
- else:
- r, c = None, None
- container = Container()
- container.AddItem(_('<em>Enter the text below, or...</em><br>'))
- container.AddItem(TextArea(varname, value or '', r, c))
- container.AddItem(_('<br><em>...specify a file to upload</em><br>'))
- container.AddItem(FileUpload(varname+'_upload', r, c))
- return container
- elif kind == config.Select:
- if params:
- values, legend, selected = params
- else:
- codes = mlist.language_codes
- legend = [config.languages.get_description(code) for code in codes]
- selected = codes.index(mlist.preferred_language)
- return SelectOptions(varname, values, legend, selected)
- elif kind == config.Topics:
- # A complex and specialized widget type that allows for setting of a
- # topic name, a mark button, a regexp text box, an "add after mark",
- # and a delete button. Yeesh! params are ignored.
- table = Table(border=0)
- # This adds the html for the entry widget
- def makebox(i, name, pattern, desc, empty=False, table=table):
- deltag = 'topic_delete_%02d' % i
- boxtag = 'topic_box_%02d' % i
- reboxtag = 'topic_rebox_%02d' % i
- desctag = 'topic_desc_%02d' % i
- wheretag = 'topic_where_%02d' % i
- addtag = 'topic_add_%02d' % i
- newtag = 'topic_new_%02d' % i
- if empty:
- table.AddRow([Center(Bold(_('Topic %(i)d'))),
- Hidden(newtag)])
- else:
- table.AddRow([Center(Bold(_('Topic %(i)d'))),
- SubmitButton(deltag, _('Delete'))])
- table.AddRow([Label(_('Topic name:')),
- TextBox(boxtag, value=name, size=30)])
- table.AddRow([Label(_('Regexp:')),
- TextArea(reboxtag, text=pattern,
- rows=4, cols=30, wrap='off')])
- table.AddRow([Label(_('Description:')),
- TextArea(desctag, text=desc,
- rows=4, cols=30, wrap='soft')])
- if not empty:
- table.AddRow([SubmitButton(addtag, _('Add new item...')),
- SelectOptions(wheretag, ('before', 'after'),
- (_('...before this one.'),
- _('...after this one.')),
- selected=1),
- ])
- table.AddRow(['<hr>'])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- # Now for each element in the existing data, create a widget
- i = 1
- data = getattr(mlist, varname)
- for name, pattern, desc, empty in data:
- makebox(i, name, pattern, desc, empty)
- i += 1
- # Add one more non-deleteable widget as the first blank entry, but
- # only if there are no real entries.
- if i == 1:
- makebox(i, '', '', '', empty=True)
- return table
- elif kind == config.HeaderFilter:
- # A complex and specialized widget type that allows for setting of a
- # spam filter rule including, a mark button, a regexp text box, an
- # "add after mark", up and down buttons, and a delete button. Yeesh!
- # params are ignored.
- table = Table(border=0)
- # This adds the html for the entry widget
- def makebox(i, pattern, action, empty=False, table=table):
- deltag = 'hdrfilter_delete_%02d' % i
- reboxtag = 'hdrfilter_rebox_%02d' % i
- actiontag = 'hdrfilter_action_%02d' % i
- wheretag = 'hdrfilter_where_%02d' % i
- addtag = 'hdrfilter_add_%02d' % i
- newtag = 'hdrfilter_new_%02d' % i
- uptag = 'hdrfilter_up_%02d' % i
- downtag = 'hdrfilter_down_%02d' % i
- if empty:
- table.AddRow([Center(Bold(_('Spam Filter Rule %(i)d'))),
- Hidden(newtag)])
- else:
- table.AddRow([Center(Bold(_('Spam Filter Rule %(i)d'))),
- SubmitButton(deltag, _('Delete'))])
- table.AddRow([Label(_('Spam Filter Regexp:')),
- TextArea(reboxtag, text=pattern,
- rows=4, cols=30, wrap='off')])
- values = [config.DEFER, config.HOLD, config.REJECT,
- config.DISCARD, config.ACCEPT]
- try:
- checked = values.index(action)
- except ValueError:
- checked = 0
- radio = RadioButtonArray(
- actiontag,
- (_('Defer'), _('Hold'), _('Reject'),
- _('Discard'), _('Accept')),
- values=values,
- checked=checked).Format()
- table.AddRow([Label(_('Action:')), radio])
- if not empty:
- table.AddRow([SubmitButton(addtag, _('Add new item...')),
- SelectOptions(wheretag, ('before', 'after'),
- (_('...before this one.'),
- _('...after this one.')),
- selected=1),
- ])
- # BAW: IWBNI we could disable the up and down buttons for the
- # first and last item respectively, but it's not easy to know
- # which is the last item, so let's not worry about that for
- # now.
- table.AddRow([SubmitButton(uptag, _('Move rule up')),
- SubmitButton(downtag, _('Move rule down'))])
- table.AddRow(['<hr>'])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- # Now for each element in the existing data, create a widget
- i = 1
- data = getattr(mlist, varname)
- for pattern, action, empty in data:
- makebox(i, pattern, action, empty)
- i += 1
- # Add one more non-deleteable widget as the first blank entry, but
- # only if there are no real entries.
- if i == 1:
- makebox(i, '', config.DEFER, empty=True)
- return table
- elif kind == config.Checkbox:
- return CheckBoxArray(varname, *params)
- else:
- assert 0, 'Bad gui widget type: %s' % kind
-
-
-
-def get_item_gui_description(mlist, category, subcat,
- varname, descr, elaboration, detailsp):
- # Return the item's description, with link to details.
- #
- # Details are not included if this is a VARHELP page, because that /is/
- # the details page!
- if detailsp:
- if subcat:
- varhelp = '?VARHELP=%s/%s/%s' % (category, subcat, varname)
- else:
- varhelp = '?VARHELP=%s/%s' % (category, varname)
- if descr == elaboration:
- linktext = _('<br>(Edit <b>%(varname)s</b>)')
- else:
- linktext = _('<br>(Details for <b>%(varname)s</b>)')
- link = Link(mlist.GetScriptURL('admin') + varhelp,
- linktext).Format()
- text = Label('%s %s' % (descr, link)).Format()
- else:
- text = Label(descr).Format()
- if varname[0] == '_':
- text += Label(_('''<br><em><strong>Note:</strong>
- setting this value performs an immediate action but does not modify
- permanent state.</em>''')).Format()
- return text
-
-
-
-def membership_options(mlist, subcat, cgidata, doc, form):
- # Show the main stuff
- adminurl = mlist.GetScriptURL('admin')
- container = Container()
- header = Table(width="100%")
- # If we're in the list subcategory, show the membership list
- if subcat == 'add':
- header.AddRow([Center(Header(2, _('Mass Subscriptions')))])
- header.AddCellInfo(header.GetCurrentRowIndex(), 0, colspan=2,
- bgcolor=config.WEB_HEADER_COLOR)
- container.AddItem(header)
- mass_subscribe(mlist, container)
- return container
- if subcat == 'remove':
- header.AddRow([Center(Header(2, _('Mass Removals')))])
- header.AddCellInfo(header.GetCurrentRowIndex(), 0, colspan=2,
- bgcolor=config.WEB_HEADER_COLOR)
- container.AddItem(header)
- mass_remove(mlist, container)
- return container
- # Otherwise...
- header.AddRow([Center(Header(2, _('Membership List')))])
- header.AddCellInfo(header.GetCurrentRowIndex(), 0, colspan=2,
- bgcolor=config.WEB_HEADER_COLOR)
- container.AddItem(header)
- # Add a "search for member" button
- table = Table(width='100%')
- link = Link('http://www.python.org/doc/current/lib/re-syntax.html',
- _('(help)')).Format()
- table.AddRow([Label(_('Find member %(link)s:')),
- TextBox('findmember',
- value=cgidata.getvalue('findmember', '')),
- SubmitButton('findmember_btn', _('Search...'))])
- container.AddItem(table)
- container.AddItem('<hr><p>')
- usertable = Table(width="90%", border='2')
- # If there are more members than allowed by chunksize, then we split the
- # membership up alphabetically. Otherwise just display them all.
- chunksz = mlist.admin_member_chunksize
- # The email addresses had /better/ be ASCII, but might be encoded in the
- # database as Unicodes.
- all = [_m.encode() for _m in mlist.getMembers()]
- all.sort(lambda x, y: cmp(x.lower(), y.lower()))
- # See if the query has a regular expression
- regexp = cgidata.getvalue('findmember', '').strip()
- if regexp:
- try:
- cre = re.compile(regexp, re.IGNORECASE)
- except re.error:
- doc.addError(_('Bad regular expression: ') + regexp)
- else:
- # BAW: There's got to be a more efficient way of doing this!
- names = [mlist.getMemberName(s) or '' for s in all]
- all = [a for n, a in zip(names, all)
- if cre.search(n) or cre.search(a)]
- chunkindex = None
- bucket = None
- actionurl = None
- if len(all) < chunksz:
- members = all
- else:
- # Split them up alphabetically, and then split the alphabetical
- # listing by chunks
- buckets = {}
- for addr in all:
- members = buckets.setdefault(addr[0].lower(), [])
- members.append(addr)
- # Now figure out which bucket we want
- bucket = None
- qs = {}
- # POST methods, even if their actions have a query string, don't get
- # put into FieldStorage's keys :-(
- qsenviron = os.environ.get('QUERY_STRING')
- if qsenviron:
- qs = cgi.parse_qs(qsenviron)
- bucket = qs.get('letter', 'a')[0].lower()
- if bucket not in digits + lowercase:
- bucket = None
- if not bucket or not buckets.has_key(bucket):
- keys = buckets.keys()
- keys.sort()
- bucket = keys[0]
- members = buckets[bucket]
- action = adminurl + '/members?letter=%s' % bucket
- if len(members) <= chunksz:
- form.set_action(action)
- else:
- i, r = divmod(len(members), chunksz)
- numchunks = i + (not not r * 1)
- # Now chunk them up
- chunkindex = 0
- if qs.has_key('chunk'):
- try:
- chunkindex = int(qs['chunk'][0])
- except ValueError:
- chunkindex = 0
- if chunkindex < 0 or chunkindex > numchunks:
- chunkindex = 0
- members = members[chunkindex*chunksz:(chunkindex+1)*chunksz]
- # And set the action URL
- form.set_action(action + '&chunk=%s' % chunkindex)
- # So now members holds all the addresses we're going to display
- allcnt = len(all)
- if bucket:
- membercnt = len(members)
- usertable.AddRow([Center(Italic(_(
- '%(allcnt)s members total, %(membercnt)s shown')))])
- else:
- usertable.AddRow([Center(Italic(_('%(allcnt)s members total')))])
- usertable.AddCellInfo(usertable.GetCurrentRowIndex(),
- usertable.GetCurrentCellIndex(),
- colspan=OPTCOLUMNS,
- bgcolor=config.WEB_ADMINITEM_COLOR)
- # Add the alphabetical links
- if bucket:
- cells = []
- for letter in digits + lowercase:
- if not buckets.get(letter):
- continue
- url = adminurl + '/members?letter=%s' % letter
- if letter == bucket:
- show = Bold('[%s]' % letter.upper()).Format()
- else:
- show = letter.upper()
- cells.append(Link(url, show).Format())
- joiner = '&nbsp;'*2 + '\n'
- usertable.AddRow([Center(joiner.join(cells))])
- usertable.AddCellInfo(usertable.GetCurrentRowIndex(),
- usertable.GetCurrentCellIndex(),
- colspan=OPTCOLUMNS,
- bgcolor=config.WEB_ADMINITEM_COLOR)
- usertable.AddRow([Center(h) for h in (_('unsub'),
- _('member address<br>member name'),
- _('mod'), _('hide'),
- _('nomail<br>[reason]'),
- _('ack'), _('not metoo'),
- _('nodupes'),
- _('digest'), _('plain'),
- _('language'))])
- rowindex = usertable.GetCurrentRowIndex()
- for i in range(OPTCOLUMNS):
- usertable.AddCellInfo(rowindex, i, bgcolor=config.WEB_ADMINITEM_COLOR)
- # Find the longest name in the list
- longest = 0
- if members:
- names = filter(None, [mlist.getMemberName(s) for s in members])
- # Make the name field at least as long as the longest email address
- longest = max([len(s) for s in names + members])
- # Abbreviations for delivery status details
- ds_abbrevs = {MemberAdaptor.UNKNOWN : _('?'),
- MemberAdaptor.BYUSER : _('U'),
- MemberAdaptor.BYADMIN : _('A'),
- MemberAdaptor.BYBOUNCE: _('B'),
- }
- # Now populate the rows
- for addr in members:
- link = Link(mlist.GetOptionsURL(addr, obscure=1),
- mlist.getMemberCPAddress(addr))
- fullname = mlist.getMemberName(addr)
- name = TextBox(addr + '_realname', fullname, size=longest).Format()
- cells = [Center(CheckBox(addr + '_unsub', 'off', 0).Format()),
- link.Format() + '<br>' +
- name +
- Hidden('user', urllib.quote(addr)).Format(),
- ]
- # Do the `mod' option
- if mlist.getMemberOption(addr, config.Moderate):
- value = 'on'
- checked = 1
- else:
- value = 'off'
- checked = 0
- box = CheckBox('%s_mod' % addr, value, checked)
- cells.append(Center(box).Format())
- for opt in ('hide', 'nomail', 'ack', 'notmetoo', 'nodupes'):
- extra = ''
- if opt == 'nomail':
- status = mlist.getDeliveryStatus(addr)
- if status == MemberAdaptor.ENABLED:
- value = 'off'
- checked = 0
- else:
- value = 'on'
- checked = 1
- extra = '[%s]' % ds_abbrevs[status]
- elif mlist.getMemberOption(addr, config.OPTINFO[opt]):
- value = 'on'
- checked = 1
- else:
- value = 'off'
- checked = 0
- box = CheckBox('%s_%s' % (addr, opt), value, checked)
- cells.append(Center(box.Format() + extra))
- # This code is less efficient than the original which did a has_key on
- # the underlying dictionary attribute. This version is slower and
- # less memory efficient. It points to a new MemberAdaptor interface
- # method.
- if addr in mlist.getRegularMemberKeys():
- cells.append(Center(CheckBox(addr + '_digest', 'off', 0).Format()))
- else:
- cells.append(Center(CheckBox(addr + '_digest', 'on', 1).Format()))
- if mlist.getMemberOption(addr, config.OPTINFO['plain']):
- value = 'on'
- checked = 1
- else:
- value = 'off'
- checked = 0
- cells.append(Center(CheckBox('%s_plain' % addr, value, checked)))
- # User's preferred language
- langpref = mlist.getMemberLanguage(addr)
- langs = mlist.language_codes
- langdescs = [_(config.languges.get_description(code))
- for code in langs]
- try:
- selected = langs.index(langpref)
- except ValueError:
- selected = 0
- cells.append(Center(SelectOptions(addr + '_language', langs,
- langdescs, selected)).Format())
- usertable.AddRow(cells)
- # Add the usertable and a legend
- legend = UnorderedList()
- legend.AddItem(
- _('<b>unsub</b> -- Click on this to unsubscribe the member.'))
- legend.AddItem(
- _("""<b>mod</b> -- The user's personal moderation flag. If this is
- set, postings from them will be moderated, otherwise they will be
- approved."""))
- legend.AddItem(
- _("""<b>hide</b> -- Is the member's address concealed on
- the list of subscribers?"""))
- legend.AddItem(_(
- """<b>nomail</b> -- Is delivery to the member disabled? If so, an
- abbreviation will be given describing the reason for the disabled
- delivery:
- <ul><li><b>U</b> -- Delivery was disabled by the user via their
- personal options page.
- <li><b>A</b> -- Delivery was disabled by the list
- administrators.
- <li><b>B</b> -- Delivery was disabled by the system due to
- excessive bouncing from the member's address.
- <li><b>?</b> -- The reason for disabled delivery isn't known.
- This is the case for all memberships which were disabled
- in older versions of Mailman.
- </ul>"""))
- legend.AddItem(
- _('''<b>ack</b> -- Does the member get acknowledgements of their
- posts?'''))
- legend.AddItem(
- _('''<b>not metoo</b> -- Does the member want to avoid copies of their
- own postings?'''))
- legend.AddItem(
- _('''<b>nodupes</b> -- Does the member want to avoid duplicates of the
- same message?'''))
- legend.AddItem(
- _('''<b>digest</b> -- Does the member get messages in digests?
- (otherwise, individual messages)'''))
- legend.AddItem(
- _('''<b>plain</b> -- If getting digests, does the member get plain
- text digests? (otherwise, MIME)'''))
- legend.AddItem(_("<b>language</b> -- Language preferred by the user"))
- addlegend = ''
- parsedqs = 0
- qsenviron = os.environ.get('QUERY_STRING')
- if qsenviron:
- qs = cgi.parse_qs(qsenviron).get('legend')
- if qs and isinstance(qs, list):
- qs = qs[0]
- if qs == 'yes':
- addlegend = 'legend=yes&'
- if addlegend:
- container.AddItem(legend.Format() + '<p>')
- container.AddItem(
- Link(adminurl + '/members/list',
- _('Click here to hide the legend for this table.')))
- else:
- container.AddItem(
- Link(adminurl + '/members/list?legend=yes',
- _('Click here to include the legend for this table.')))
- container.AddItem(Center(usertable))
-
- # There may be additional chunks
- if chunkindex is not None:
- buttons = []
- url = adminurl + '/members?%sletter=%s&' % (addlegend, bucket)
- footer = _('''<p><em>To view more members, click on the appropriate
- range listed below:</em>''')
- chunkmembers = buckets[bucket]
- last = len(chunkmembers)
- for i in range(numchunks):
- if i == chunkindex:
- continue
- start = chunkmembers[i*chunksz]
- end = chunkmembers[min((i+1)*chunksz, last)-1]
- link = Link(url + 'chunk=%d' % i, _('from %(start)s to %(end)s'))
- buttons.append(link)
- buttons = UnorderedList(*buttons)
- container.AddItem(footer + buttons.Format() + '<p>')
- return container
-
-
-
-def mass_subscribe(mlist, container):
- # MASS SUBSCRIBE
- GREY = config.WEB_ADMINITEM_COLOR
- table = Table(width='90%')
- table.AddRow([
- Label(_('Subscribe these users now or invite them?')),
- RadioButtonArray('subscribe_or_invite',
- (_('Subscribe'), _('Invite')),
- 0, values=(0, 1))
- ])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, bgcolor=GREY)
- table.AddCellInfo(table.GetCurrentRowIndex(), 1, bgcolor=GREY)
- table.AddRow([
- Label(_('Send welcome messages to new subscribees?')),
- RadioButtonArray('send_welcome_msg_to_this_batch',
- (_('No'), _('Yes')),
- mlist.send_welcome_msg,
- values=(0, 1))
- ])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, bgcolor=GREY)
- table.AddCellInfo(table.GetCurrentRowIndex(), 1, bgcolor=GREY)
- table.AddRow([
- Label(_('Send notifications of new subscriptions to the list owner?')),
- RadioButtonArray('send_notifications_to_list_owner',
- (_('No'), _('Yes')),
- mlist.admin_notify_mchanges,
- values=(0,1))
- ])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, bgcolor=GREY)
- table.AddCellInfo(table.GetCurrentRowIndex(), 1, bgcolor=GREY)
- table.AddRow([Italic(_('Enter one address per line below...'))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- table.AddRow([Center(TextArea(name='subscribees',
- rows=10, cols='70%', wrap=None))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- table.AddRow([Italic(Label(_('...or specify a file to upload:'))),
- FileUpload('subscribees_upload', cols='50')])
- container.AddItem(Center(table))
- # Invitation text
- table.AddRow(['&nbsp;', '&nbsp;'])
- table.AddRow([Italic(_("""Below, enter additional text to be added to the
- top of your invitation or the subscription notification. Include at least
- one blank line at the end..."""))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- table.AddRow([Center(TextArea(name='invitation',
- rows=10, cols='70%', wrap=None))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
-
-
-
-def mass_remove(mlist, container):
- # MASS UNSUBSCRIBE
- GREY = config.WEB_ADMINITEM_COLOR
- table = Table(width='90%')
- table.AddRow([
- Label(_('Send unsubscription acknowledgement to the user?')),
- RadioButtonArray('send_unsub_ack_to_this_batch',
- (_('No'), _('Yes')),
- 0, values=(0, 1))
- ])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, bgcolor=GREY)
- table.AddCellInfo(table.GetCurrentRowIndex(), 1, bgcolor=GREY)
- table.AddRow([
- Label(_('Send notifications to the list owner?')),
- RadioButtonArray('send_unsub_notifications_to_list_owner',
- (_('No'), _('Yes')),
- mlist.admin_notify_mchanges,
- values=(0, 1))
- ])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, bgcolor=GREY)
- table.AddCellInfo(table.GetCurrentRowIndex(), 1, bgcolor=GREY)
- table.AddRow([Italic(_('Enter one address per line below...'))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- table.AddRow([Center(TextArea(name='unsubscribees',
- rows=10, cols='70%', wrap=None))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- table.AddRow([Italic(Label(_('...or specify a file to upload:'))),
- FileUpload('unsubscribees_upload', cols='50')])
- container.AddItem(Center(table))
-
-
-
-def password_inputs(mlist):
- adminurl = mlist.GetScriptURL('admin')
- table = Table(cellspacing=3, cellpadding=4)
- table.AddRow([Center(Header(2, _('Change list ownership passwords')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2,
- bgcolor=config.WEB_HEADER_COLOR)
- table.AddRow([_("""\
-The <em>list administrators</em> are the people who have ultimate control over
-all parameters of this mailing list. They are able to change any list
-configuration variable available through these administration web pages.
-
-<p>The <em>list moderators</em> have more limited permissions; they are not
-able to change any list configuration variable, but they are allowed to tend
-to pending administration requests, including approving or rejecting held
-subscription requests, and disposing of held postings. Of course, the
-<em>list administrators</em> can also tend to pending requests.
-
-<p>In order to split the list ownership duties into administrators and
-moderators, you must set a separate moderator password in the fields below,
-and also provide the email addresses of the list moderators in the
-<a href="%(adminurl)s/general">general options section</a>.""")])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- # Set up the admin password table on the left
- atable = Table(border=0, cellspacing=3, cellpadding=4,
- bgcolor=config.WEB_ADMINPW_COLOR)
- atable.AddRow([Label(_('Enter new administrator password:')),
- PasswordBox('newpw', size=20)])
- atable.AddRow([Label(_('Confirm administrator password:')),
- PasswordBox('confirmpw', size=20)])
- # Set up the moderator password table on the right
- mtable = Table(border=0, cellspacing=3, cellpadding=4,
- bgcolor=config.WEB_ADMINPW_COLOR)
- mtable.AddRow([Label(_('Enter new moderator password:')),
- PasswordBox('newmodpw', size=20)])
- mtable.AddRow([Label(_('Confirm moderator password:')),
- PasswordBox('confirmmodpw', size=20)])
- # Add these tables to the overall password table
- table.AddRow([atable, mtable])
- return table
-
-
-
-def submit_button(name='submit'):
- table = Table(border=0, cellspacing=0, cellpadding=2)
- table.AddRow([Bold(SubmitButton(name, _('Submit Your Changes')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, align='middle')
- return table
-
-
-
-def change_options(mlist, category, subcat, cgidata, doc):
- def safeint(formvar, defaultval=None):
- try:
- return int(cgidata.getvalue(formvar))
- except (ValueError, TypeError):
- return defaultval
- confirmed = 0
- # Handle changes to the list moderator password. Do this before checking
- # the new admin password, since the latter will force a reauthentication.
- new = cgidata.getvalue('newmodpw', '').strip()
- confirm = cgidata.getvalue('confirmmodpw', '').strip()
- if new or confirm:
- if new == confirm:
- mlist.mod_password = passwords.make_secret(
- new, config.PASSWORD_SCHEME)
- # No re-authentication necessary because the moderator's
- # password doesn't get you into these pages.
- else:
- doc.addError(_('Moderator passwords did not match'))
- # Handle changes to the list administrator password
- new = cgidata.getvalue('newpw', '').strip()
- confirm = cgidata.getvalue('confirmpw', '').strip()
- if new or confirm:
- if new == confirm:
- mlist.password = passwords.make_secret(new, config.PASSWORD_SCHEME)
- # Set new cookie
- print mlist.MakeCookie(config.AuthListAdmin)
- else:
- doc.addError(_('Administrator passwords did not match'))
- # Give the individual gui item a chance to process the form data
- categories = mlist.GetConfigCategories()
- label, gui = categories[category]
- # BAW: We handle the membership page special... for now.
- if category <> 'members':
- gui.handleForm(mlist, category, subcat, cgidata, doc)
- # mass subscription, removal processing for members category
- subscribers = ''
- subscribers += cgidata.getvalue('subscribees', '')
- subscribers += cgidata.getvalue('subscribees_upload', '')
- if subscribers:
- entries = filter(None, [n.strip() for n in subscribers.splitlines()])
- send_welcome_msg = safeint('send_welcome_msg_to_this_batch',
- mlist.send_welcome_msg)
- send_admin_notif = safeint('send_notifications_to_list_owner',
- mlist.admin_notify_mchanges)
- # Default is to subscribe
- subscribe_or_invite = safeint('subscribe_or_invite', 0)
- invitation = cgidata.getvalue('invitation', '')
- digest = mlist.digest_is_default
- if not mlist.digestable:
- digest = 0
- if not mlist.nondigestable:
- digest = 1
- subscribe_errors = []
- subscribe_success = []
- # Now cruise through all the subscribees and do the deed. BAW: we
- # should limit the number of "Successfully subscribed" status messages
- # we display. Try uploading a file with 10k names -- it takes a while
- # to render the status page.
- for entry in entries:
- fullname, address = parseaddr(entry)
- # Canonicalize the full name
- fullname = Utils.canonstr(fullname, mlist.preferred_language)
- userdesc = UserDesc(address, fullname,
- Utils.MakeRandomPassword(),
- digest, mlist.preferred_language)
- try:
- if subscribe_or_invite:
- if mlist.isMember(address):
- raise Errors.MMAlreadyAMember
- else:
- mlist.InviteNewMember(userdesc, invitation)
- else:
- mlist.ApprovedAddMember(userdesc, send_welcome_msg,
- send_admin_notif, invitation,
- whence='admin mass sub')
- except Errors.MMAlreadyAMember:
- subscribe_errors.append((entry, _('Already a member')))
- except Errors.InvalidEmailAddress:
- if userdesc.address == '':
- subscribe_errors.append((_('&lt;blank line&gt;'),
- _('Bad/Invalid email address')))
- else:
- subscribe_errors.append((entry,
- _('Bad/Invalid email address')))
- except Errors.MembershipIsBanned, pattern:
- subscribe_errors.append(
- (entry, _('Banned address (matched %(pattern)s)')))
- else:
- member = Utils.uncanonstr(formataddr((fullname, address)))
- subscribe_success.append(Utils.websafe(member))
- if subscribe_success:
- if subscribe_or_invite:
- doc.AddItem(Header(5, _('Successfully invited:')))
- else:
- doc.AddItem(Header(5, _('Successfully subscribed:')))
- doc.AddItem(UnorderedList(*subscribe_success))
- doc.AddItem('<p>')
- if subscribe_errors:
- if subscribe_or_invite:
- doc.AddItem(Header(5, _('Error inviting:')))
- else:
- doc.AddItem(Header(5, _('Error subscribing:')))
- items = ['%s -- %s' % (x0, x1) for x0, x1 in subscribe_errors]
- doc.AddItem(UnorderedList(*items))
- doc.AddItem('<p>')
- # Unsubscriptions
- removals = ''
- if cgidata.has_key('unsubscribees'):
- removals += cgidata['unsubscribees'].value
- if cgidata.has_key('unsubscribees_upload') and \
- cgidata['unsubscribees_upload'].value:
- removals += cgidata['unsubscribees_upload'].value
- if removals:
- names = filter(None, [n.strip() for n in removals.splitlines()])
- send_unsub_notifications = int(
- cgidata['send_unsub_notifications_to_list_owner'].value)
- userack = int(
- cgidata['send_unsub_ack_to_this_batch'].value)
- unsubscribe_errors = []
- unsubscribe_success = []
- for addr in names:
- try:
- mlist.ApprovedDeleteMember(
- addr, whence='admin mass unsub',
- admin_notif=send_unsub_notifications,
- userack=userack)
- unsubscribe_success.append(addr)
- except Errors.NotAMemberError:
- unsubscribe_errors.append(addr)
- if unsubscribe_success:
- doc.AddItem(Header(5, _('Successfully Unsubscribed:')))
- doc.AddItem(UnorderedList(*unsubscribe_success))
- doc.AddItem('<p>')
- if unsubscribe_errors:
- doc.AddItem(Header(3, Bold(FontAttr(
- _('Cannot unsubscribe non-members:'),
- color='#ff0000', size='+2')).Format()))
- doc.AddItem(UnorderedList(*unsubscribe_errors))
- doc.AddItem('<p>')
- # See if this was a moderation bit operation
- if cgidata.has_key('allmodbit_btn'):
- val = cgidata.getvalue('allmodbit_val')
- try:
- val = int(val)
- except VallueError:
- val = None
- if val not in (0, 1):
- doc.addError(_('Bad moderation flag value'))
- else:
- for member in mlist.getMembers():
- mlist.setMemberOption(member, config.Moderate, val)
- # do the user options for members category
- if cgidata.has_key('setmemberopts_btn') and cgidata.has_key('user'):
- user = cgidata['user']
- if isinstance(user, list):
- users = []
- for ui in range(len(user)):
- users.append(urllib.unquote(user[ui].value))
- else:
- users = [urllib.unquote(user.value)]
- errors = []
- removes = []
- for user in users:
- if cgidata.has_key('%s_unsub' % user):
- try:
- mlist.ApprovedDeleteMember(user, whence='member mgt page')
- removes.append(user)
- except Errors.NotAMemberError:
- errors.append((user, _('Not subscribed')))
- continue
- if not mlist.isMember(user):
- doc.addError(_('Ignoring changes to deleted member: %(user)s'),
- tag=_('Warning: '))
- continue
- value = cgidata.has_key('%s_digest' % user)
- try:
- mlist.setMemberOption(user, config.Digests, value)
- except (Errors.AlreadyReceivingDigests,
- Errors.AlreadyReceivingRegularDeliveries,
- Errors.CantDigestError,
- Errors.MustDigestError):
- # BAW: Hmm...
- pass
-
- newname = cgidata.getvalue(user+'_realname', '')
- newname = Utils.canonstr(newname, mlist.preferred_language)
- mlist.setMemberName(user, newname)
-
- newlang = cgidata.getvalue(user+'_language')
- oldlang = mlist.getMemberLanguage(user)
- if (newlang not in config.languages.enabled_codes
- and newlang <> oldlang):
- # Then
- mlist.setMemberLanguage(user, newlang)
-
- moderate = not not cgidata.getvalue(user+'_mod')
- mlist.setMemberOption(user, config.Moderate, moderate)
-
- # Set the `nomail' flag, but only if the user isn't already
- # disabled (otherwise we might change BYUSER into BYADMIN).
- if cgidata.has_key('%s_nomail' % user):
- if mlist.getDeliveryStatus(user) == MemberAdaptor.ENABLED:
- mlist.setDeliveryStatus(user, MemberAdaptor.BYADMIN)
- else:
- mlist.setDeliveryStatus(user, MemberAdaptor.ENABLED)
- for opt in ('hide', 'ack', 'notmetoo', 'nodupes', 'plain'):
- opt_code = config.OPTINFO[opt]
- if cgidata.has_key('%s_%s' % (user, opt)):
- mlist.setMemberOption(user, opt_code, 1)
- else:
- mlist.setMemberOption(user, opt_code, 0)
- # Give some feedback on who's been removed
- if removes:
- doc.AddItem(Header(5, _('Successfully Removed:')))
- doc.AddItem(UnorderedList(*removes))
- doc.AddItem('<p>')
- if errors:
- doc.AddItem(Header(5, _("Error Unsubscribing:")))
- items = ['%s -- %s' % (x[0], x[1]) for x in errors]
- doc.AddItem(apply(UnorderedList, tuple((items))))
- doc.AddItem("<p>")
diff --git a/mailman/web/Cgi/admindb.py b/mailman/web/Cgi/admindb.py
deleted file mode 100644
index 5d756fc78..000000000
--- a/mailman/web/Cgi/admindb.py
+++ /dev/null
@@ -1,813 +0,0 @@
-# Copyright (C) 1998-2009 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""Produce and process the pending-approval items for a list."""
-
-import os
-import cgi
-import sys
-import time
-import email
-import errno
-import logging
-
-from urllib import quote_plus, unquote_plus
-
-from Mailman import Errors
-from Mailman import MailList
-from Mailman import Message
-from Mailman import Utils
-from Mailman import i18n
-from Mailman.Cgi import Auth
-from Mailman.Handlers.Moderate import ModeratedMemberPost
-from Mailman.ListAdmin import readMessage
-from Mailman.configuration import config
-from Mailman.htmlformat import *
-from Mailman.interfaces import RequestType
-
-EMPTYSTRING = ''
-NL = '\n'
-
-# Set up i18n. Until we know which list is being requested, we use the
-# server's default.
-_ = i18n._
-i18n.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
-EXCERPT_HEIGHT = 10
-EXCERPT_WIDTH = 76
-
-log = logging.getLogger('mailman.error')
-
-
-
-def helds_by_sender(mlist):
- bysender = {}
- requests = config.db.get_list_requests(mlist)
- for request in requests.of_type(RequestType.held_message):
- key, data = requests.get_request(request.id)
- sender = data.get('sender')
- assert sender is not None, (
- 'No sender for held message: %s' % request.id)
- bysender.setdefault(sender, []).append(request.id)
- return bysender
-
-
-def hacky_radio_buttons(btnname, labels, values, defaults, spacing=3):
- # We can't use a RadioButtonArray here because horizontal placement can be
- # confusing to the user and vertical placement takes up too much
- # real-estate. This is a hack!
- space = '&nbsp;' * spacing
- btns = Table(cellspacing='5', cellpadding='0')
- btns.AddRow([space + text + space for text in labels])
- btns.AddRow([Center(RadioButton(btnname, value, default))
- for value, default in zip(values, defaults)])
- return btns
-
-
-
-def main():
- # Figure out which list is being requested
- parts = Utils.GetPathPieces()
- if not parts:
- handle_no_list()
- return
-
- listname = parts[0].lower()
- try:
- mlist = MailList.MailList(listname, lock=0)
- except Errors.MMListError, e:
- # Avoid cross-site scripting attacks
- safelistname = Utils.websafe(listname)
- handle_no_list(_('No such list <em>%(safelistname)s</em>'))
- log.error('No such list "%s": %s\n', listname, e)
- return
-
- # Now that we know which list to use, set the system's language to it.
- i18n.set_language(mlist.preferred_language)
-
- # Make sure the user is authorized to see this page.
- cgidata = cgi.FieldStorage(keep_blank_values=1)
-
- if not mlist.WebAuthenticate((config.AuthListAdmin,
- config.AuthListModerator,
- config.AuthSiteAdmin),
- cgidata.getvalue('adminpw', '')):
- if cgidata.has_key('adminpw'):
- # This is a re-authorization attempt
- msg = Bold(FontSize('+1', _('Authorization failed.'))).Format()
- else:
- msg = ''
- Auth.loginpage(mlist, 'admindb', msg=msg)
- return
-
- # Set up the results document
- doc = Document()
- doc.set_language(mlist.preferred_language)
-
- # See if we're requesting all the messages for a particular sender, or if
- # we want a specific held message.
- sender = None
- msgid = None
- details = None
- envar = os.environ.get('QUERY_STRING')
- if envar:
- # POST methods, even if their actions have a query string, don't get
- # put into FieldStorage's keys :-(
- qs = cgi.parse_qs(envar).get('sender')
- if qs and isinstance(qs, list):
- sender = qs[0]
- qs = cgi.parse_qs(envar).get('msgid')
- if qs and isinstance(qs, list):
- msgid = qs[0]
- qs = cgi.parse_qs(envar).get('details')
- if qs and isinstance(qs, list):
- details = qs[0]
-
- mlist.Lock()
- try:
- realname = mlist.real_name
- if not cgidata.keys() or cgidata.has_key('admlogin'):
- # If this is not a form submission (i.e. there are no keys in the
- # form) or it's a login, then we don't need to do much special.
- doc.SetTitle(_('%(realname)s Administrative Database'))
- elif not details:
- # This is a form submission
- doc.SetTitle(_('%(realname)s Administrative Database Results'))
- process_form(mlist, doc, cgidata)
- # Now print the results and we're done. Short circuit for when there
- # are no pending requests, but be sure to save the results!
- if config.db.requests.get_list_requests(mlist).count == 0:
- title = _('%(realname)s Administrative Database')
- doc.SetTitle(title)
- doc.AddItem(Header(2, title))
- doc.AddItem(_('There are no pending requests.'))
- doc.AddItem(' ')
- doc.AddItem(Link(mlist.GetScriptURL('admindb'),
- _('Click here to reload this page.')))
- doc.AddItem(mlist.GetMailmanFooter())
- print doc.Format()
- mlist.Save()
- return
-
- admindburl = mlist.GetScriptURL('admindb')
- form = Form(admindburl)
- # Add the instructions template
- if details == 'instructions':
- doc.AddItem(Header(
- 2, _('Detailed instructions for the administrative database')))
- else:
- doc.AddItem(Header(
- 2,
- _('Administrative requests for mailing list:')
- + ' <em>%s</em>' % mlist.real_name))
- if details <> 'instructions':
- form.AddItem(Center(SubmitButton('submit', _('Submit All Data'))))
- requestsdb = config.db.get_list_requests(mlist)
- message_count = requestsdb.count_of(RequestType.held_message)
- if not (details or sender or msgid or message_count == 0):
- form.AddItem(Center(
- CheckBox('discardalldefersp', 0).Format() +
- '&nbsp;' +
- _('Discard all messages marked <em>Defer</em>')
- ))
- # Add a link back to the overview, if we're not viewing the overview!
- adminurl = mlist.GetScriptURL('admin')
- d = {'listname' : mlist.real_name,
- 'detailsurl': admindburl + '?details=instructions',
- 'summaryurl': admindburl,
- 'viewallurl': admindburl + '?details=all',
- 'adminurl' : adminurl,
- 'filterurl' : adminurl + '/privacy/sender',
- }
- addform = 1
- if sender:
- esender = Utils.websafe(sender)
- d['description'] = _("all of %(esender)s's held messages.")
- doc.AddItem(Utils.maketext('admindbpreamble.html', d,
- raw=1, mlist=mlist))
- show_sender_requests(mlist, form, sender)
- elif msgid:
- d['description'] = _('a single held message.')
- doc.AddItem(Utils.maketext('admindbpreamble.html', d,
- raw=1, mlist=mlist))
- show_message_requests(mlist, form, msgid)
- elif details == 'all':
- d['description'] = _('all held messages.')
- doc.AddItem(Utils.maketext('admindbpreamble.html', d,
- raw=1, mlist=mlist))
- show_detailed_requests(mlist, form)
- elif details == 'instructions':
- doc.AddItem(Utils.maketext('admindbdetails.html', d,
- raw=1, mlist=mlist))
- addform = 0
- else:
- # Show a summary of all requests
- doc.AddItem(Utils.maketext('admindbsummary.html', d,
- raw=1, mlist=mlist))
- num = show_pending_subs(mlist, form)
- num += show_pending_unsubs(mlist, form)
- num += show_helds_overview(mlist, form)
- addform = num > 0
- # Finish up the document, adding buttons to the form
- if addform:
- doc.AddItem(form)
- form.AddItem('<hr>')
- if not (details or sender or msgid or nomessages):
- form.AddItem(Center(
- CheckBox('discardalldefersp', 0).Format() +
- '&nbsp;' +
- _('Discard all messages marked <em>Defer</em>')
- ))
- form.AddItem(Center(SubmitButton('submit', _('Submit All Data'))))
- doc.AddItem(mlist.GetMailmanFooter())
- print doc.Format()
- # Commit all changes
- mlist.Save()
- finally:
- mlist.Unlock()
-
-
-
-def handle_no_list(msg=''):
- # Print something useful if no list was given.
- doc = Document()
- doc.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
- header = _('Mailman Administrative Database Error')
- doc.SetTitle(header)
- doc.AddItem(Header(2, header))
- doc.AddItem(msg)
- url = Utils.ScriptURL('admin')
- link = Link(url, _('list of available mailing lists.')).Format()
- doc.AddItem(_('You must specify a list name. Here is the %(link)s'))
- doc.AddItem('<hr>')
- doc.AddItem(MailmanLogo())
- print doc.Format()
-
-
-
-def show_pending_subs(mlist, form):
- # Add the subscription request section
- requestsdb = config.db.get_list_requests(mlist)
- if requestsdb.count_of(RequestType.subscription) == 0:
- return 0
- form.AddItem('<hr>')
- form.AddItem(Center(Header(2, _('Subscription Requests'))))
- table = Table(border=2)
- table.AddRow([Center(Bold(_('Address/name'))),
- Center(Bold(_('Your decision'))),
- Center(Bold(_('Reason for refusal')))
- ])
- # Alphabetical order by email address
- byaddrs = {}
- for request in requestsdb.of_type(RequestType.subscription):
- key, data = requestsdb.get_request(requst.id)
- addr = data['addr']
- byaddrs.setdefault(addr, []).append(request.id)
- addrs = sorted(byaddrs)
- num = 0
- for addr, ids in byaddrs.items():
- # Eliminate duplicates
- for id in ids[1:]:
- mlist.HandleRequest(id, config.DISCARD)
- id = ids[0]
- key, data = requestsdb.get_request(id)
- time = data['time']
- addr = data['addr']
- fullname = data['fullname']
- passwd = data['passwd']
- digest = data['digest']
- lang = data['lang']
- fullname = Utils.uncanonstr(fullname, mlist.preferred_language)
- radio = RadioButtonArray(id, (_('Defer'),
- _('Approve'),
- _('Reject'),
- _('Discard')),
- values=(config.DEFER,
- config.SUBSCRIBE,
- config.REJECT,
- config.DISCARD),
- checked=0).Format()
- if addr not in mlist.ban_list:
- radio += '<br>' + CheckBox('ban-%d' % id, 1).Format() + \
- '&nbsp;' + _('Permanently ban from this list')
- # While the address may be a unicode, it must be ascii
- paddr = addr.encode('us-ascii', 'replace')
- table.AddRow(['%s<br><em>%s</em>' % (paddr, fullname),
- radio,
- TextBox('comment-%d' % id, size=40)
- ])
- num += 1
- if num > 0:
- form.AddItem(table)
- return num
-
-
-
-def show_pending_unsubs(mlist, form):
- # Add the pending unsubscription request section
- lang = mlist.preferred_language
- requestsdb = config.db.get_list_requests(mlist)
- if requestsdb.count_of(RequestType.unsubscription) == 0:
- return 0
- table = Table(border=2)
- table.AddRow([Center(Bold(_('User address/name'))),
- Center(Bold(_('Your decision'))),
- Center(Bold(_('Reason for refusal')))
- ])
- # Alphabetical order by email address
- byaddrs = {}
- for request in requestsdb.of_type(RequestType.unsubscription):
- key, data = requestsdb.get_request(request.id)
- addr = data['addr']
- byaddrs.setdefault(addr, []).append(request.id)
- addrs = sorted(byaddrs)
- num = 0
- for addr, ids in byaddrs.items():
- # Eliminate duplicates
- for id in ids[1:]:
- mlist.HandleRequest(id, config.DISCARD)
- id = ids[0]
- key, data = requestsdb.get_record(id)
- addr = data['addr']
- try:
- fullname = Utils.uncanonstr(mlist.getMemberName(addr), lang)
- except Errors.NotAMemberError:
- # They must have been unsubscribed elsewhere, so we can just
- # discard this record.
- mlist.HandleRequest(id, config.DISCARD)
- continue
- num += 1
- table.AddRow(['%s<br><em>%s</em>' % (addr, fullname),
- RadioButtonArray(id, (_('Defer'),
- _('Approve'),
- _('Reject'),
- _('Discard')),
- values=(config.DEFER,
- config.UNSUBSCRIBE,
- config.REJECT,
- config.DISCARD),
- checked=0),
- TextBox('comment-%d' % id, size=45)
- ])
- if num > 0:
- form.AddItem('<hr>')
- form.AddItem(Center(Header(2, _('Unsubscription Requests'))))
- form.AddItem(table)
- return num
-
-
-
-def show_helds_overview(mlist, form):
- # Sort the held messages by sender
- bysender = helds_by_sender(mlist)
- if not bysender:
- return 0
- form.AddItem('<hr>')
- form.AddItem(Center(Header(2, _('Held Messages'))))
- # Add the by-sender overview tables
- admindburl = mlist.GetScriptURL('admindb')
- table = Table(border=0)
- form.AddItem(table)
- senders = bysender.keys()
- senders.sort()
- for sender in senders:
- qsender = quote_plus(sender)
- esender = Utils.websafe(sender)
- senderurl = admindburl + '?sender=' + qsender
- # The encompassing sender table
- stable = Table(border=1)
- stable.AddRow([Center(Bold(_('From:')).Format() + esender)])
- stable.AddCellInfo(stable.GetCurrentRowIndex(), 0, colspan=2)
- left = Table(border=0)
- left.AddRow([_('Action to take on all these held messages:')])
- left.AddCellInfo(left.GetCurrentRowIndex(), 0, colspan=2)
- btns = hacky_radio_buttons(
- 'senderaction-' + qsender,
- (_('Defer'), _('Accept'), _('Reject'), _('Discard')),
- (config.DEFER, config.APPROVE, config.REJECT, config.DISCARD),
- (1, 0, 0, 0))
- left.AddRow([btns])
- left.AddCellInfo(left.GetCurrentRowIndex(), 0, colspan=2)
- left.AddRow([
- CheckBox('senderpreserve-' + qsender, 1).Format() +
- '&nbsp;' +
- _('Preserve messages for the site administrator')
- ])
- left.AddCellInfo(left.GetCurrentRowIndex(), 0, colspan=2)
- left.AddRow([
- CheckBox('senderforward-' + qsender, 1).Format() +
- '&nbsp;' +
- _('Forward messages (individually) to:')
- ])
- left.AddCellInfo(left.GetCurrentRowIndex(), 0, colspan=2)
- left.AddRow([
- TextBox('senderforwardto-' + qsender,
- value=mlist.GetOwnerEmail())
- ])
- left.AddCellInfo(left.GetCurrentRowIndex(), 0, colspan=2)
- # If the sender is a member and the message is being held due to a
- # moderation bit, give the admin a chance to clear the member's mod
- # bit. If this sender is not a member and is not already on one of
- # the sender filters, then give the admin a chance to add this sender
- # to one of the filters.
- if mlist.isMember(sender):
- if mlist.getMemberOption(sender, config.Moderate):
- left.AddRow([
- CheckBox('senderclearmodp-' + qsender, 1).Format() +
- '&nbsp;' +
- _("Clear this member's <em>moderate</em> flag")
- ])
- else:
- left.AddRow(
- [_('<em>The sender is now a member of this list</em>')])
- left.AddCellInfo(left.GetCurrentRowIndex(), 0, colspan=2)
- elif sender not in (mlist.accept_these_nonmembers +
- mlist.hold_these_nonmembers +
- mlist.reject_these_nonmembers +
- mlist.discard_these_nonmembers):
- left.AddRow([
- CheckBox('senderfilterp-' + qsender, 1).Format() +
- '&nbsp;' +
- _('Add <b>%(esender)s</b> to one of these sender filters:')
- ])
- left.AddCellInfo(left.GetCurrentRowIndex(), 0, colspan=2)
- btns = hacky_radio_buttons(
- 'senderfilter-' + qsender,
- (_('Accepts'), _('Holds'), _('Rejects'), _('Discards')),
- (config.ACCEPT, config.HOLD, config.REJECT, config.DISCARD),
- (0, 0, 0, 1))
- left.AddRow([btns])
- left.AddCellInfo(left.GetCurrentRowIndex(), 0, colspan=2)
- if sender not in mlist.ban_list:
- left.AddRow([
- CheckBox('senderbanp-' + qsender, 1).Format() +
- '&nbsp;' +
- _("""Ban <b>%(esender)s</b> from ever subscribing to this
- mailing list""")])
- left.AddCellInfo(left.GetCurrentRowIndex(), 0, colspan=2)
- right = Table(border=0)
- right.AddRow([
- _("""Click on the message number to view the individual
- message, or you can """) +
- Link(senderurl, _('view all messages from %(esender)s')).Format()
- ])
- right.AddCellInfo(right.GetCurrentRowIndex(), 0, colspan=2)
- right.AddRow(['&nbsp;', '&nbsp;'])
- counter = 1
- for id in bysender[sender]:
- key, data = requestsdb.get_record(id)
- ptime = data['ptime']
- sender = data['sender']
- subject = data['subject']
- reason = data['reason']
- filename = data['filename']
- msgdata = data['msgdata']
- # BAW: This is really the size of the message pickle, which should
- # be close, but won't be exact. Sigh, good enough.
- try:
- size = os.path.getsize(os.path.join(config.DATA_DIR, filename))
- except OSError, e:
- if e.errno <> errno.ENOENT: raise
- # This message must have gotten lost, i.e. it's already been
- # handled by the time we got here.
- mlist.HandleRequest(id, config.DISCARD)
- continue
- dispsubj = Utils.oneline(
- subject, Utils.GetCharSet(mlist.preferred_language))
- t = Table(border=0)
- t.AddRow([Link(admindburl + '?msgid=%d' % id, '[%d]' % counter),
- Bold(_('Subject:')),
- Utils.websafe(dispsubj)
- ])
- t.AddRow(['&nbsp;', Bold(_('Size:')), str(size) + _(' bytes')])
- if reason:
- reason = _(reason)
- else:
- reason = _('not available')
- t.AddRow(['&nbsp;', Bold(_('Reason:')), reason])
- # Include the date we received the message, if available
- when = msgdata.get('received_time')
- if when:
- t.AddRow(['&nbsp;', Bold(_('Received:')),
- time.ctime(when)])
- counter += 1
- right.AddRow([t])
- stable.AddRow([left, right])
- table.AddRow([stable])
- return 1
-
-
-
-def show_sender_requests(mlist, form, sender):
- bysender = helds_by_sender(mlist)
- if not bysender:
- return
- sender_ids = bysender.get(sender)
- if sender_ids is None:
- # BAW: should we print an error message?
- return
- total = len(sender_ids)
- requestsdb = config.db.get_list_requests(mlist)
- for i, id in enumerate(sender_ids):
- key, data = requestsdb.get_record(id)
- show_post_requests(mlist, id, data, total, count + 1, form)
-
-
-
-def show_message_requests(mlist, form, id):
- requestdb = config.db.get_list_requests(mlist)
- try:
- id = int(id)
- info = requestdb.get_record(id)
- except (ValueError, KeyError):
- # BAW: print an error message?
- return
- show_post_requests(mlist, id, info, 1, 1, form)
-
-
-
-def show_detailed_requests(mlist, form):
- requestsdb = config.db.get_list_requests(mlist)
- total = requestsdb.count_of(RequestType.held_message)
- all = requestsdb.of_type(RequestType.held_message)
- for i, request in enumerate(all):
- key, data = requestdb.get_request(request.id)
- show_post_requests(mlist, request.id, data, total, i + 1, form)
-
-
-
-def show_post_requests(mlist, id, info, total, count, form):
- # For backwards compatibility with pre 2.0beta3
- if len(info) == 5:
- ptime, sender, subject, reason, filename = info
- msgdata = {}
- else:
- ptime, sender, subject, reason, filename, msgdata = info
- form.AddItem('<hr>')
- # Header shown on each held posting (including count of total)
- msg = _('Posting Held for Approval')
- if total <> 1:
- msg += _(' (%(count)d of %(total)d)')
- form.AddItem(Center(Header(2, msg)))
- # We need to get the headers and part of the textual body of the message
- # being held. The best way to do this is to use the email Parser to get
- # an actual object, which will be easier to deal with. We probably could
- # just do raw reads on the file.
- try:
- msg = readMessage(os.path.join(config.DATA_DIR, filename))
- except IOError, e:
- if e.errno <> errno.ENOENT:
- raise
- form.AddItem(_('<em>Message with id #%(id)d was lost.'))
- form.AddItem('<p>')
- # BAW: kludge to remove id from requests.db.
- try:
- mlist.HandleRequest(id, config.DISCARD)
- except Errors.LostHeldMessage:
- pass
- return
- except email.Errors.MessageParseError:
- form.AddItem(_('<em>Message with id #%(id)d is corrupted.'))
- # BAW: Should we really delete this, or shuttle it off for site admin
- # to look more closely at?
- form.AddItem('<p>')
- # BAW: kludge to remove id from requests.db.
- try:
- mlist.HandleRequest(id, config.DISCARD)
- except Errors.LostHeldMessage:
- pass
- return
- # Get the header text and the message body excerpt
- lines = []
- chars = 0
- # A negative value means, include the entire message regardless of size
- limit = config.ADMINDB_PAGE_TEXT_LIMIT
- for line in email.Iterators.body_line_iterator(msg):
- lines.append(line)
- chars += len(line)
- if chars > limit > 0:
- break
- # Negative values mean display the entire message, regardless of size
- if limit > 0:
- body = EMPTYSTRING.join(lines)[:config.ADMINDB_PAGE_TEXT_LIMIT]
- else:
- body = EMPTYSTRING.join(lines)
- # Get message charset and try encode in list charset
- mcset = msg.get_param('charset', 'us-ascii').lower()
- lcset = Utils.GetCharSet(mlist.preferred_language)
- if mcset <> lcset:
- try:
- body = unicode(body, mcset).encode(lcset)
- except (LookupError, UnicodeError, ValueError):
- pass
- hdrtxt = NL.join(['%s: %s' % (k, v) for k, v in msg.items()])
- hdrtxt = Utils.websafe(hdrtxt)
- # Okay, we've reconstituted the message just fine. Now for the fun part!
- t = Table(cellspacing=0, cellpadding=0, width='100%')
- t.AddRow([Bold(_('From:')), sender])
- row, col = t.GetCurrentRowIndex(), t.GetCurrentCellIndex()
- t.AddCellInfo(row, col-1, align='right')
- t.AddRow([Bold(_('Subject:')),
- Utils.websafe(Utils.oneline(subject, lcset))])
- t.AddCellInfo(row+1, col-1, align='right')
- t.AddRow([Bold(_('Reason:')), _(reason)])
- t.AddCellInfo(row+2, col-1, align='right')
- when = msgdata.get('received_time')
- if when:
- t.AddRow([Bold(_('Received:')), time.ctime(when)])
- t.AddCellInfo(row+2, col-1, align='right')
- # We can't use a RadioButtonArray here because horizontal placement can be
- # confusing to the user and vertical placement takes up too much
- # real-estate. This is a hack!
- buttons = Table(cellspacing="5", cellpadding="0")
- buttons.AddRow(map(lambda x, s='&nbsp;'*5: s+x+s,
- (_('Defer'), _('Approve'), _('Reject'), _('Discard'))))
- buttons.AddRow([Center(RadioButton(id, config.DEFER, 1)),
- Center(RadioButton(id, config.APPROVE, 0)),
- Center(RadioButton(id, config.REJECT, 0)),
- Center(RadioButton(id, config.DISCARD, 0)),
- ])
- t.AddRow([Bold(_('Action:')), buttons])
- t.AddCellInfo(row+3, col-1, align='right')
- t.AddRow(['&nbsp;',
- CheckBox('preserve-%d' % id, 'on', 0).Format() +
- '&nbsp;' + _('Preserve message for site administrator')
- ])
- t.AddRow(['&nbsp;',
- CheckBox('forward-%d' % id, 'on', 0).Format() +
- '&nbsp;' + _('Additionally, forward this message to: ') +
- TextBox('forward-addr-%d' % id, size=47,
- value=mlist.GetOwnerEmail()).Format()
- ])
- notice = msgdata.get('rejection_notice', _('[No explanation given]'))
- t.AddRow([
- Bold(_('If you reject this post,<br>please explain (optional):')),
- TextArea('comment-%d' % id, rows=4, cols=EXCERPT_WIDTH,
- text = Utils.wrap(_(notice), column=80))
- ])
- row, col = t.GetCurrentRowIndex(), t.GetCurrentCellIndex()
- t.AddCellInfo(row, col-1, align='right')
- t.AddRow([Bold(_('Message Headers:')),
- TextArea('headers-%d' % id, hdrtxt,
- rows=EXCERPT_HEIGHT, cols=EXCERPT_WIDTH, readonly=1)])
- row, col = t.GetCurrentRowIndex(), t.GetCurrentCellIndex()
- t.AddCellInfo(row, col-1, align='right')
- t.AddRow([Bold(_('Message Excerpt:')),
- TextArea('fulltext-%d' % id, Utils.websafe(body),
- rows=EXCERPT_HEIGHT, cols=EXCERPT_WIDTH, readonly=1)])
- t.AddCellInfo(row+1, col-1, align='right')
- form.AddItem(t)
- form.AddItem('<p>')
-
-
-
-def process_form(mlist, doc, cgidata):
- senderactions = {}
- # Sender-centric actions
- for k in cgidata.keys():
- for prefix in ('senderaction-', 'senderpreserve-', 'senderforward-',
- 'senderforwardto-', 'senderfilterp-', 'senderfilter-',
- 'senderclearmodp-', 'senderbanp-'):
- if k.startswith(prefix):
- action = k[:len(prefix)-1]
- sender = unquote_plus(k[len(prefix):])
- value = cgidata.getvalue(k)
- senderactions.setdefault(sender, {})[action] = value
- # discard-all-defers
- try:
- discardalldefersp = cgidata.getvalue('discardalldefersp', 0)
- except ValueError:
- discardalldefersp = 0
- for sender in senderactions.keys():
- actions = senderactions[sender]
- # Handle what to do about all this sender's held messages
- try:
- action = int(actions.get('senderaction', config.DEFER))
- except ValueError:
- action = config.DEFER
- if action == config.DEFER and discardalldefersp:
- action = config.DISCARD
- if action in (config.DEFER, config.APPROVE,
- config.REJECT, config.DISCARD):
- preserve = actions.get('senderpreserve', 0)
- forward = actions.get('senderforward', 0)
- forwardaddr = actions.get('senderforwardto', '')
- comment = _('No reason given')
- bysender = helds_by_sender(mlist)
- for id in bysender.get(sender, []):
- try:
- mlist.HandleRequest(id, action, comment, preserve,
- forward, forwardaddr)
- except (KeyError, Errors.LostHeldMessage):
- # That's okay, it just means someone else has already
- # updated the database while we were staring at the page,
- # so just ignore it
- continue
- # Now see if this sender should be added to one of the nonmember
- # sender filters.
- if actions.get('senderfilterp', 0):
- try:
- which = int(actions.get('senderfilter'))
- except ValueError:
- # Bogus form
- which = 'ignore'
- if which == config.ACCEPT:
- mlist.accept_these_nonmembers.append(sender)
- elif which == config.HOLD:
- mlist.hold_these_nonmembers.append(sender)
- elif which == config.REJECT:
- mlist.reject_these_nonmembers.append(sender)
- elif which == config.DISCARD:
- mlist.discard_these_nonmembers.append(sender)
- # Otherwise, it's a bogus form, so ignore it
- # And now see if we're to clear the member's moderation flag.
- if actions.get('senderclearmodp', 0):
- try:
- mlist.setMemberOption(sender, config.Moderate, 0)
- except Errors.NotAMemberError:
- # This person's not a member any more. Oh well.
- pass
- # And should this address be banned?
- if actions.get('senderbanp', 0):
- if sender not in mlist.ban_list:
- mlist.ban_list.append(sender)
- # Now, do message specific actions
- banaddrs = []
- erroraddrs = []
- for k in cgidata.keys():
- formv = cgidata[k]
- if isinstance(formv, list):
- continue
- try:
- v = int(formv.value)
- request_id = int(k)
- except ValueError:
- continue
- if v not in (config.DEFER, config.APPROVE, config.REJECT,
- config.DISCARD, config.SUBSCRIBE, config.UNSUBSCRIBE,
- config.ACCEPT, config.HOLD):
- continue
- # Get the action comment and reasons if present.
- commentkey = 'comment-%d' % request_id
- preservekey = 'preserve-%d' % request_id
- forwardkey = 'forward-%d' % request_id
- forwardaddrkey = 'forward-addr-%d' % request_id
- bankey = 'ban-%d' % request_id
- # Defaults
- comment = _('[No reason given]')
- preserve = 0
- forward = 0
- forwardaddr = ''
- if cgidata.has_key(commentkey):
- comment = cgidata[commentkey].value
- if cgidata.has_key(preservekey):
- preserve = cgidata[preservekey].value
- if cgidata.has_key(forwardkey):
- forward = cgidata[forwardkey].value
- if cgidata.has_key(forwardaddrkey):
- forwardaddr = cgidata[forwardaddrkey].value
- # Should we ban this address? Do this check before handling the
- # request id because that will evict the record.
- requestsdb = config.db.get_list_requests(mlist)
- if cgidata.getvalue(bankey):
- key, data = requestsdb.get_record(request_id)
- sender = data['sender']
- if sender not in mlist.ban_list:
- mlist.ban_list.append(sender)
- # Handle the request id
- try:
- mlist.HandleRequest(request_id, v, comment,
- preserve, forward, forwardaddr)
- except (KeyError, Errors.LostHeldMessage):
- # That's okay, it just means someone else has already updated the
- # database while we were staring at the page, so just ignore it
- continue
- except Errors.MMAlreadyAMember, v:
- erroraddrs.append(v)
- except Errors.MembershipIsBanned, pattern:
- data = requestsdb.get_record(request_id)
- sender = data['sender']
- banaddrs.append((sender, pattern))
- # save the list and print the results
- doc.AddItem(Header(2, _('Database Updated...')))
- if erroraddrs:
- for addr in erroraddrs:
- doc.AddItem(`addr` + _(' is already a member') + '<br>')
- if banaddrs:
- for addr, patt in banaddrs:
- doc.AddItem(_('%(addr)s is banned (matched: %(patt)s)') + '<br>')
diff --git a/mailman/web/Cgi/confirm.py b/mailman/web/Cgi/confirm.py
deleted file mode 100644
index 188e43068..000000000
--- a/mailman/web/Cgi/confirm.py
+++ /dev/null
@@ -1,834 +0,0 @@
-# Copyright (C) 2001-2009 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""Confirm a pending action via URL."""
-
-import cgi
-import time
-import signal
-import logging
-
-from Mailman import Errors
-from Mailman import MailList
-from Mailman import Pending
-from Mailman import i18n
-from Mailman.UserDesc import UserDesc
-from Mailman.configuration import config
-from Mailman.htmlformat import *
-
-# Set up i18n
-_ = i18n._
-i18n.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
-log = logging.getLogger('mailman.error')
-
-
-
-def main():
- doc = Document()
- doc.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
- parts = Utils.GetPathPieces()
- if not parts or len(parts) < 1:
- bad_confirmation(doc)
- doc.AddItem(MailmanLogo())
- print doc.Format()
- return
-
- listname = parts[0].lower()
- try:
- mlist = MailList.MailList(listname, lock=0)
- except Errors.MMListError, e:
- # Avoid cross-site scripting attacks
- safelistname = Utils.websafe(listname)
- bad_confirmation(doc, _('No such list <em>%(safelistname)s</em>'))
- doc.AddItem(MailmanLogo())
- print doc.Format()
- log.error('No such list "%s": %s', listname, e)
- return
-
- # Set the language for the list
- i18n.set_language(mlist.preferred_language)
- doc.set_language(mlist.preferred_language)
-
- # Get the form data to see if this is a second-step confirmation
- cgidata = cgi.FieldStorage(keep_blank_values=1)
- cookie = cgidata.getvalue('cookie')
- if cookie == '':
- ask_for_cookie(mlist, doc, _('Confirmation string was empty.'))
- return
-
- if not cookie and len(parts) == 2:
- cookie = parts[1]
-
- if len(parts) > 2:
- bad_confirmation(doc)
- doc.AddItem(mlist.GetMailmanFooter())
- print doc.Format()
- return
-
- if not cookie:
- ask_for_cookie(mlist, doc)
- return
-
- days = int(config.PENDING_REQUEST_LIFE / config.days(1) + 0.5)
- confirmurl = mlist.GetScriptURL('confirm')
- # Avoid cross-site scripting attacks
- safecookie = Utils.websafe(cookie)
- badconfirmstr = _('''<b>Invalid confirmation string:</b>
- %(safecookie)s.
-
- <p>Note that confirmation strings expire approximately
- %(days)s days after the initial subscription request. If your
- confirmation has expired, please try to re-submit your subscription.
- Otherwise, <a href="%(confirmurl)s">re-enter</a> your confirmation
- string.''')
-
- content = mlist.pend_confirm(cookie, expunge=False)
- if content is None:
- bad_confirmation(doc, badconfirmstr)
- doc.AddItem(mlist.GetMailmanFooter())
- print doc.Format()
- return
-
- try:
- if content[0] == Pending.SUBSCRIPTION:
- if cgidata.getvalue('cancel'):
- subscription_cancel(mlist, doc, cookie)
- elif cgidata.getvalue('submit'):
- subscription_confirm(mlist, doc, cookie, cgidata)
- else:
- subscription_prompt(mlist, doc, cookie, content[1])
- elif content[0] == Pending.UNSUBSCRIPTION:
- try:
- if cgidata.getvalue('cancel'):
- unsubscription_cancel(mlist, doc, cookie)
- elif cgidata.getvalue('submit'):
- unsubscription_confirm(mlist, doc, cookie)
- else:
- unsubscription_prompt(mlist, doc, cookie, *content[1:])
- except Errors.NotAMemberError:
- doc.addError(_("""The address requesting unsubscription is not
- a member of the mailing list. Perhaps you have already been
- unsubscribed, e.g. by the list administrator?"""))
- # Expunge this record from the pending database.
- expunge(mlist, cookie)
- elif content[0] == Pending.CHANGE_OF_ADDRESS:
- if cgidata.getvalue('cancel'):
- addrchange_cancel(mlist, doc, cookie)
- elif cgidata.getvalue('submit'):
- addrchange_confirm(mlist, doc, cookie)
- else:
- # Watch out for users who have unsubscribed themselves in the
- # meantime!
- try:
- addrchange_prompt(mlist, doc, cookie, *content[1:])
- except Errors.NotAMemberError:
- doc.addError(_("""The address requesting to be changed has
- been subsequently unsubscribed. This request has been
- cancelled."""))
- # Expunge this record from the pending database.
- expunge(mlist, cookie)
- elif content[0] == Pending.HELD_MESSAGE:
- if cgidata.getvalue('cancel'):
- heldmsg_cancel(mlist, doc, cookie)
- elif cgidata.getvalue('submit'):
- heldmsg_confirm(mlist, doc, cookie)
- else:
- heldmsg_prompt(mlist, doc, cookie, *content[1:])
- elif content[0] == Pending.RE_ENABLE:
- if cgidata.getvalue('cancel'):
- reenable_cancel(mlist, doc, cookie)
- elif cgidata.getvalue('submit'):
- reenable_confirm(mlist, doc, cookie)
- else:
- reenable_prompt(mlist, doc, cookie, *content[1:])
- else:
- bad_confirmation(doc, _('System error, bad content: %(content)s'))
- except Errors.MMBadConfirmation:
- bad_confirmation(doc, badconfirmstr)
-
- doc.AddItem(mlist.GetMailmanFooter())
- print doc.Format()
-
-
-
-def bad_confirmation(doc, extra=''):
- title = _('Bad confirmation string')
- doc.SetTitle(title)
- doc.AddItem(Header(3, Bold(FontAttr(title, color='#ff0000', size='+2'))))
- doc.AddItem(extra)
-
-
-def expunge(mlist, cookie):
- # Expunge this record from the list's pending database. This requires
- # that the list lock be acquired, however the list doesn't need to be
- # saved because this operation doesn't touch the config.pck file.
- mlist.Lock()
- try:
- mlist.pend_confirm(cookie, expunge=True)
- finally:
- mlist.Unlock()
-
-
-
-def ask_for_cookie(mlist, doc, extra=''):
- title = _('Enter confirmation cookie')
- doc.SetTitle(title)
- form = Form(mlist.GetScriptURL('confirm'))
- table = Table(border=0, width='100%')
- table.AddRow([Center(Bold(FontAttr(title, size='+1')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0,
- colspan=2, bgcolor=config.WEB_HEADER_COLOR)
-
- if extra:
- table.AddRow([Bold(FontAttr(extra, size='+1'))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
-
- # Add cookie entry box
- table.AddRow([_("""Please enter the confirmation string
- (i.e. <em>cookie</em>) that you received in your email message, in the box
- below. Then hit the <em>Submit</em> button to proceed to the next
- confirmation step.""")])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- table.AddRow([Label(_('Confirmation string:')),
- TextBox('cookie')])
- table.AddRow([Center(SubmitButton('submit_cookie', _('Submit')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- form.AddItem(table)
- doc.AddItem(form)
- doc.AddItem(mlist.GetMailmanFooter())
- print doc.Format()
-
-
-
-def subscription_prompt(mlist, doc, cookie, userdesc):
- email = userdesc.address
- password = userdesc.password
- digest = userdesc.digest
- lang = userdesc.language
- name = Utils.uncanonstr(userdesc.fullname, lang)
- i18n.set_language(lang)
- doc.set_language(lang)
- title = _('Confirm subscription request')
- doc.SetTitle(title)
-
- form = Form(mlist.GetScriptURL('confirm'))
- table = Table(border=0, width='100%')
- table.AddRow([Center(Bold(FontAttr(title, size='+1')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0,
- colspan=2, bgcolor=config.WEB_HEADER_COLOR)
-
- listname = mlist.real_name
- # This is the normal, no-confirmation required results text.
- #
- # We do things this way so we don't have to reformat this paragraph, which
- # would mess up translations. If you modify this text for other reasons,
- # please refill the paragraph, and clean up the logic.
- result = _("""Your confirmation is required in order to complete the
- subscription request to the mailing list <em>%(listname)s</em>. Your
- subscription settings are shown below; make any necessary changes and hit
- <em>Subscribe</em> to complete the confirmation process. Once you've
- confirmed your subscription request, you will be shown your account
- options page which you can use to further customize your membership
- options.
-
- <p>Note: your password will be emailed to you once your subscription is
- confirmed. You can change it by visiting your personal options page.
-
- <p>Or hit <em>Cancel my subscription request</em> if you no longer want to
- subscribe to this list.""") + '<p><hr>'
- if mlist.subscribe_policy in (2, 3):
- # Confirmation is required
- result = _("""Your confirmation is required in order to continue with
- the subscription request to the mailing list <em>%(listname)s</em>.
- Your subscription settings are shown below; make any necessary changes
- and hit <em>Subscribe to list ...</em> to complete the confirmation
- process. Once you've confirmed your subscription request, the
- moderator must approve or reject your membership request. You will
- receive notice of their decision.
-
- <p>Note: your password will be emailed to you once your subscription
- is confirmed. You can change it by visiting your personal options
- page.
-
- <p>Or, if you've changed your mind and do not want to subscribe to
- this mailing list, you can hit <em>Cancel my subscription
- request</em>.""") + '<p><hr>'
- table.AddRow([result])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
-
- table.AddRow([Label(_('Your email address:')), email])
- table.AddRow([Label(_('Your real name:')),
- TextBox('realname', name)])
-## table.AddRow([Label(_('Password:')),
-## PasswordBox('password', password)])
-## table.AddRow([Label(_('Password (confirm):')),
-## PasswordBox('pwconfirm', password)])
- # Only give them a choice to receive digests if they actually have a
- # choice <wink>.
- if mlist.nondigestable and mlist.digestable:
- table.AddRow([Label(_('Receive digests?')),
- RadioButtonArray('digests', (_('No'), _('Yes')),
- checked=digest, values=(0, 1))])
- langs = mlist.language_codes
- values = [_(config.languages.get_description(code)) for code in langs]
- try:
- selected = langs.index(lang)
- except ValueError:
- selected = lang.index(mlist.preferred_language)
- table.AddRow([Label(_('Preferred language:')),
- SelectOptions('language', langs, values, selected)])
- table.AddRow([Hidden('cookie', cookie)])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- table.AddRow([
- Label(SubmitButton('cancel', _('Cancel my subscription request'))),
- SubmitButton('submit', _('Subscribe to list %(listname)s'))
- ])
- form.AddItem(table)
- doc.AddItem(form)
-
-
-
-def subscription_cancel(mlist, doc, cookie):
- mlist.Lock()
- try:
- # Discard this cookie
- userdesc = mlist.pend_confirm(cookie)[1]
- finally:
- mlist.Unlock()
- lang = userdesc.language
- i18n.set_language(lang)
- doc.set_language(lang)
- doc.AddItem(_('You have canceled your subscription request.'))
-
-
-
-def subscription_confirm(mlist, doc, cookie, cgidata):
- # See the comment in admin.py about the need for the signal
- # handler.
- def sigterm_handler(signum, frame, mlist=mlist):
- mlist.Unlock()
- sys.exit(0)
-
- listname = mlist.real_name
- mlist.Lock()
- try:
- try:
- # Some pending values may be overridden in the form. email of
- # course is hardcoded. ;)
- lang = cgidata.getvalue('language')
- if lang not in config.languages.enabled_codes:
- lang = mlist.preferred_language
- i18n.set_language(lang)
- doc.set_language(lang)
- if cgidata.has_key('digests'):
- try:
- digest = int(cgidata.getvalue('digests'))
- except ValueError:
- digest = None
- else:
- digest = None
- userdesc = mlist.pend_confirm(cookie, expunge=False)[1]
- fullname = cgidata.getvalue('realname', None)
- if fullname is not None:
- fullname = Utils.canonstr(fullname, lang)
- overrides = UserDesc(fullname=fullname, digest=digest, lang=lang)
- userdesc += overrides
- op, addr, pw, digest, lang = mlist.ProcessConfirmation(
- cookie, userdesc)
- except Errors.MMNeedApproval:
- title = _('Awaiting moderator approval')
- doc.SetTitle(title)
- doc.AddItem(Header(3, Bold(FontAttr(title, size='+2'))))
- doc.AddItem(_("""\
- You have successfully confirmed your subscription request to the
- mailing list %(listname)s, however final approval is required from
- the list moderator before you will be subscribed. Your request
- has been forwarded to the list moderator, and you will be notified
- of the moderator's decision."""))
- except Errors.NotAMemberError:
- bad_confirmation(doc, _('''Invalid confirmation string. It is
- possible that you are attempting to confirm a request for an
- address that has already been unsubscribed.'''))
- except Errors.MMAlreadyAMember:
- doc.addError(_("You are already a member of this mailing list!"))
- except Errors.MembershipIsBanned:
- owneraddr = mlist.GetOwnerEmail()
- doc.addError(_("""You are currently banned from subscribing to
- this list. If you think this restriction is erroneous, please
- contact the list owners at %(owneraddr)s."""))
- except Errors.HostileSubscriptionError:
- doc.addError(_("""\
- You were not invited to this mailing list. The invitation has
- been discarded, and both list administrators have been
- alerted."""))
- else:
- # Use the user's preferred language
- i18n.set_language(lang)
- doc.set_language(lang)
- # The response
- listname = mlist.real_name
- title = _('Subscription request confirmed')
- optionsurl = mlist.GetOptionsURL(addr)
- doc.SetTitle(title)
- doc.AddItem(Header(3, Bold(FontAttr(title, size='+2'))))
- doc.AddItem(_('''\
- You have successfully confirmed your subscription request for
- "%(addr)s" to the %(listname)s mailing list. A separate
- confirmation message will be sent to your email address, along
- with your password, and other useful information and links.
-
- <p>You can now
- <a href="%(optionsurl)s">proceed to your membership login
- page</a>.'''))
- mlist.Save()
- finally:
- mlist.Unlock()
-
-
-
-def unsubscription_cancel(mlist, doc, cookie):
- # Expunge this record from the pending database
- expunge(mlist, cookie)
- doc.AddItem(_('You have canceled your unsubscription request.'))
-
-
-
-def unsubscription_confirm(mlist, doc, cookie):
- # See the comment in admin.py about the need for the signal
- # handler.
- def sigterm_handler(signum, frame, mlist=mlist):
- mlist.Unlock()
- sys.exit(0)
-
- mlist.Lock()
- try:
- try:
- # Do this in two steps so we can get the preferred language for
- # the user who is unsubscribing.
- op, addr = mlist.pend_confirm(cookie, expunge=False)
- lang = mlist.getMemberLanguage(addr)
- i18n.set_language(lang)
- doc.set_language(lang)
- op, addr = mlist.ProcessConfirmation(cookie)
- except Errors.NotAMemberError:
- bad_confirmation(doc, _('''Invalid confirmation string. It is
- possible that you are attempting to confirm a request for an
- address that has already been unsubscribed.'''))
- else:
- # The response
- listname = mlist.real_name
- title = _('Unsubscription request confirmed')
- listinfourl = mlist.GetScriptURL('listinfo')
- doc.SetTitle(title)
- doc.AddItem(Header(3, Bold(FontAttr(title, size='+2'))))
- doc.AddItem(_("""\
- You have successfully unsubscribed from the %(listname)s mailing
- list. You can now <a href="%(listinfourl)s">visit the list's main
- information page</a>."""))
- mlist.Save()
- finally:
- mlist.Unlock()
-
-
-
-def unsubscription_prompt(mlist, doc, cookie, addr):
- title = _('Confirm unsubscription request')
- doc.SetTitle(title)
- lang = mlist.getMemberLanguage(addr)
- i18n.set_language(lang)
- doc.set_language(lang)
-
- form = Form(mlist.GetScriptURL('confirm'))
- table = Table(border=0, width='100%')
- table.AddRow([Center(Bold(FontAttr(title, size='+1')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0,
- colspan=2, bgcolor=config.WEB_HEADER_COLOR)
-
- listname = mlist.real_name
- fullname = mlist.getMemberName(addr)
- if fullname is None:
- fullname = _('<em>Not available</em>')
- else:
- fullname = Utils.uncanonstr(fullname, lang)
- table.AddRow([_("""Your confirmation is required in order to complete the
- unsubscription request from the mailing list <em>%(listname)s</em>. You
- are currently subscribed with
-
- <ul><li><b>Real name:</b> %(fullname)s
- <li><b>Email address:</b> %(addr)s
- </ul>
-
- Hit the <em>Unsubscribe</em> button below to complete the confirmation
- process.
-
- <p>Or hit <em>Cancel and discard</em> to cancel this unsubscription
- request.""") + '<p><hr>'])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- table.AddRow([Hidden('cookie', cookie)])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- table.AddRow([SubmitButton('submit', _('Unsubscribe')),
- SubmitButton('cancel', _('Cancel and discard'))])
-
- form.AddItem(table)
- doc.AddItem(form)
-
-
-
-def addrchange_cancel(mlist, doc, cookie):
- # Expunge this record from the pending database
- expunge(mlist, cookie)
- doc.AddItem(_('You have canceled your change of address request.'))
-
-
-
-def addrchange_confirm(mlist, doc, cookie):
- # See the comment in admin.py about the need for the signal
- # handler.
- def sigterm_handler(signum, frame, mlist=mlist):
- mlist.Unlock()
- sys.exit(0)
-
- mlist.Lock()
- try:
- try:
- # Do this in two steps so we can get the preferred language for
- # the user who is unsubscribing.
- op, oldaddr, newaddr, globally = mlist.pend_confirm(
- cookie, expunge=False)
- lang = mlist.getMemberLanguage(oldaddr)
- i18n.set_language(lang)
- doc.set_language(lang)
- op, oldaddr, newaddr = mlist.ProcessConfirmation(cookie)
- except Errors.NotAMemberError:
- bad_confirmation(doc, _('''Invalid confirmation string. It is
- possible that you are attempting to confirm a request for an
- address that has already been unsubscribed.'''))
- except Errors.MembershipIsBanned:
- owneraddr = mlist.GetOwnerEmail()
- realname = mlist.real_name
- doc.addError(_("""%(newaddr)s is banned from subscribing to the
- %(realname)s list. If you think this restriction is erroneous,
- please contact the list owners at %(owneraddr)s."""))
- else:
- # The response
- listname = mlist.real_name
- title = _('Change of address request confirmed')
- optionsurl = mlist.GetOptionsURL(newaddr)
- doc.SetTitle(title)
- doc.AddItem(Header(3, Bold(FontAttr(title, size='+2'))))
- doc.AddItem(_("""\
- You have successfully changed your address on the %(listname)s
- mailing list from <b>%(oldaddr)s</b> to <b>%(newaddr)s</b>. You
- can now <a href="%(optionsurl)s">proceed to your membership
- login page</a>."""))
- mlist.Save()
- finally:
- mlist.Unlock()
-
-
-
-def addrchange_prompt(mlist, doc, cookie, oldaddr, newaddr, globally):
- title = _('Confirm change of address request')
- doc.SetTitle(title)
- lang = mlist.getMemberLanguage(oldaddr)
- i18n.set_language(lang)
- doc.set_language(lang)
-
- form = Form(mlist.GetScriptURL('confirm'))
- table = Table(border=0, width='100%')
- table.AddRow([Center(Bold(FontAttr(title, size='+1')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0,
- colspan=2, bgcolor=config.WEB_HEADER_COLOR)
-
- listname = mlist.real_name
- fullname = mlist.getMemberName(oldaddr)
- if fullname is None:
- fullname = _('<em>Not available</em>')
- else:
- fullname = Utils.uncanonstr(fullname, lang)
- if globally:
- globallys = _('globally')
- else:
- globallys = ''
- table.AddRow([_("""Your confirmation is required in order to complete the
- change of address request for the mailing list <em>%(listname)s</em>. You
- are currently subscribed with
-
- <ul><li><b>Real name:</b> %(fullname)s
- <li><b>Old email address:</b> %(oldaddr)s
- </ul>
-
- and you have requested to %(globallys)s change your email address to
-
- <ul><li><b>New email address:</b> %(newaddr)s
- </ul>
-
- Hit the <em>Change address</em> button below to complete the confirmation
- process.
-
- <p>Or hit <em>Cancel and discard</em> to cancel this change of address
- request.""") + '<p><hr>'])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- table.AddRow([Hidden('cookie', cookie)])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- table.AddRow([SubmitButton('submit', _('Change address')),
- SubmitButton('cancel', _('Cancel and discard'))])
-
- form.AddItem(table)
- doc.AddItem(form)
-
-
-
-def heldmsg_cancel(mlist, doc, cookie):
- title = _('Continue awaiting approval')
- doc.SetTitle(title)
- table = Table(border=0, width='100%')
- table.AddRow([Center(Bold(FontAttr(title, size='+1')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0,
- bgcolor=config.WEB_HEADER_COLOR)
- # Expunge this record from the pending database.
- expunge(mlist, cookie)
- table.AddRow([_('''Okay, the list moderator will still have the
- opportunity to approve or reject this message.''')])
- doc.AddItem(table)
-
-
-
-def heldmsg_confirm(mlist, doc, cookie):
- # See the comment in admin.py about the need for the signal
- # handler.
- def sigterm_handler(signum, frame, mlist=mlist):
- mlist.Unlock()
- sys.exit(0)
-
- mlist.Lock()
- try:
- try:
- # Do this in two steps so we can get the preferred language for
- # the user who posted the message.
- op, id = mlist.pend_confirm(cookie)
- requestsdb = config.db.get_list_requests(mlist)
- key, data = requestsdb.get_record(id)
- sender = data['sender']
- msgsubject = data['msgsubject']
- subject = Utils.websafe(msgsubject)
- lang = mlist.getMemberLanguage(sender)
- i18n.set_language(lang)
- doc.set_language(lang)
- # Discard the message
- mlist.HandleRequest(id, config.DISCARD,
- _('Sender discarded message via web.'))
- except Errors.LostHeldMessage:
- bad_confirmation(doc, _('''The held message with the Subject:
- header <em>%(subject)s</em> could not be found. The most likely
- reason for this is that the list moderator has already approved or
- rejected the message. You were not able to cancel it in
- time.'''))
- else:
- # The response
- listname = mlist.real_name
- title = _('Posted message canceled')
- doc.SetTitle(title)
- doc.AddItem(Header(3, Bold(FontAttr(title, size='+2'))))
- doc.AddItem(_('''\
- You have successfully canceled the posting of your message with
- the Subject: header <em>%(subject)s</em> to the mailing list
- %(listname)s.'''))
- mlist.Save()
- finally:
- mlist.Unlock()
-
-
-
-def heldmsg_prompt(mlist, doc, cookie, id):
- title = _('Cancel held message posting')
- doc.SetTitle(title)
- form = Form(mlist.GetScriptURL('confirm'))
- table = Table(border=0, width='100%')
- table.AddRow([Center(Bold(FontAttr(title, size='+1')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0,
- colspan=2, bgcolor=config.WEB_HEADER_COLOR)
- # Blarg. The list must be locked in order to interact with the ListAdmin
- # database, even for read-only. See the comment in admin.py about the
- # need for the signal handler.
- def sigterm_handler(signum, frame, mlist=mlist):
- mlist.Unlock()
- sys.exit(0)
- # Get the record, but watch for KeyErrors which mean the admin has already
- # disposed of this message.
- mlist.Lock()
- requestdb = config.db.get_list_requests(mlist)
- try:
- try:
- key, data = requestdb.get_record(id)
- except KeyError:
- data = None
- finally:
- mlist.Unlock()
-
- if data is None:
- bad_confirmation(doc, _("""The held message you were referred to has
- already been handled by the list administrator."""))
- return
-
- # Unpack the data and present the confirmation message
- ign, sender, msgsubject, givenreason, ign, ign = data
- # Now set the language to the sender's preferred.
- lang = mlist.getMemberLanguage(sender)
- i18n.set_language(lang)
- doc.set_language(lang)
-
- subject = Utils.websafe(msgsubject)
- reason = Utils.websafe(_(givenreason))
- listname = mlist.real_name
- table.AddRow([_('''Your confirmation is required in order to cancel the
- posting of your message to the mailing list <em>%(listname)s</em>:
-
- <ul><li><b>Sender:</b> %(sender)s
- <li><b>Subject:</b> %(subject)s
- <li><b>Reason:</b> %(reason)s
- </ul>
-
- Hit the <em>Cancel posting</em> button to discard the posting.
-
- <p>Or hit the <em>Continue awaiting approval</em> button to continue to
- allow the list moderator to approve or reject the message.''')
- + '<p><hr>'])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- table.AddRow([Hidden('cookie', cookie)])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- table.AddRow([SubmitButton('submit', _('Cancel posting')),
- SubmitButton('cancel', _('Continue awaiting approval'))])
-
- form.AddItem(table)
- doc.AddItem(form)
-
-
-
-def reenable_cancel(mlist, doc, cookie):
- # Don't actually discard this cookie, since the user may decide to
- # re-enable their membership at a future time, and we may be sending out
- # future notifications with this cookie value.
- doc.AddItem(_("""You have canceled the re-enabling of your membership. If
- we continue to receive bounces from your address, it could be deleted from
- this mailing list."""))
-
-
-
-def reenable_confirm(mlist, doc, cookie):
- # See the comment in admin.py about the need for the signal
- # handler.
- def sigterm_handler(signum, frame, mlist=mlist):
- mlist.Unlock()
- sys.exit(0)
-
- mlist.Lock()
- try:
- try:
- # Do this in two steps so we can get the preferred language for
- # the user who is unsubscribing.
- op, listname, addr = mlist.pend_confirm(cookie, expunge=False)
- lang = mlist.getMemberLanguage(addr)
- i18n.set_language(lang)
- doc.set_language(lang)
- op, addr = mlist.ProcessConfirmation(cookie)
- except Errors.NotAMemberError:
- bad_confirmation(doc, _('''Invalid confirmation string. It is
- possible that you are attempting to confirm a request for an
- address that has already been unsubscribed.'''))
- else:
- # The response
- listname = mlist.real_name
- title = _('Membership re-enabled.')
- optionsurl = mlist.GetOptionsURL(addr)
- doc.SetTitle(title)
- doc.AddItem(Header(3, Bold(FontAttr(title, size='+2'))))
- doc.AddItem(_("""\
- You have successfully re-enabled your membership in the
- %(listname)s mailing list. You can now <a
- href="%(optionsurl)s">visit your member options page</a>.
- """))
- mlist.Save()
- finally:
- mlist.Unlock()
-
-
-
-def reenable_prompt(mlist, doc, cookie, list, member):
- title = _('Re-enable mailing list membership')
- doc.SetTitle(title)
- form = Form(mlist.GetScriptURL('confirm'))
- table = Table(border=0, width='100%')
- table.AddRow([Center(Bold(FontAttr(title, size='+1')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0,
- colspan=2, bgcolor=config.WEB_HEADER_COLOR)
-
- lang = mlist.getMemberLanguage(member)
- i18n.set_language(lang)
- doc.set_language(lang)
-
- realname = mlist.real_name
- info = mlist.getBounceInfo(member)
- if not info:
- listinfourl = mlist.GetScriptURL('listinfo')
- # They've already be unsubscribed
- table.AddRow([_("""We're sorry, but you have already been unsubscribed
- from this mailing list. To re-subscribe, please visit the
- <a href="%(listinfourl)s">list information page</a>.""")])
- return
-
- date = time.strftime('%A, %B %d, %Y',
- time.localtime(time.mktime(info.date + (0,)*6)))
- daysleft = int(info.noticesleft *
- mlist.bounce_you_are_disabled_warnings_interval /
- config.days(1))
- # BAW: for consistency this should be changed to 'fullname' or the above
- # 'fullname's should be changed to 'username'. Don't want to muck with
- # the i18n catalogs though.
- username = mlist.getMemberName(member)
- if username is None:
- username = _('<em>not available</em>')
- else:
- username = Utils.uncanonstr(username, lang)
-
- table.AddRow([_("""Your membership in the %(realname)s mailing list is
- currently disabled due to excessive bounces. Your confirmation is
- required in order to re-enable delivery to your address. We have the
- following information on file:
-
- <ul><li><b>Member address:</b> %(member)s
- <li><b>Member name:</b> %(username)s
- <li><b>Last bounce received on:</b> %(date)s
- <li><b>Approximate number of days before you are permanently removed
- from this list:</b> %(daysleft)s
- </ul>
-
- Hit the <em>Re-enable membership</em> button to resume receiving postings
- from the mailing list. Or hit the <em>Cancel</em> button to defer
- re-enabling your membership.
- """)])
-
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- table.AddRow([Hidden('cookie', cookie)])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2)
- table.AddRow([SubmitButton('submit', _('Re-enable membership')),
- SubmitButton('cancel', _('Cancel'))])
-
- form.AddItem(table)
- doc.AddItem(form)
diff --git a/mailman/web/Cgi/create.py b/mailman/web/Cgi/create.py
deleted file mode 100644
index 4bb650957..000000000
--- a/mailman/web/Cgi/create.py
+++ /dev/null
@@ -1,400 +0,0 @@
-# Copyright (C) 2001-2009 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""Create mailing lists through the web."""
-
-import cgi
-import sha
-import sys
-import logging
-
-from Mailman import Errors
-from Mailman import MailList
-from Mailman import Message
-from Mailman import i18n
-from Mailman import passwords
-from Mailman.configuration import config
-from Mailman.htmlformat import *
-
-# Set up i18n
-_ = i18n._
-i18n.set_language(config.DEFAULT_SERVER_LANGUAGE)
-__i18n_templates__ = True
-
-log = logging.getLogger('mailman.error')
-
-
-
-def main():
- doc = Document()
- doc.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
- cgidata = cgi.FieldStorage()
- parts = Utils.GetPathPieces()
- if parts:
- # Bad URL specification
- title = _('Bad URL specification')
- doc.SetTitle(title)
- doc.AddItem(
- Header(3, Bold(FontAttr(title, color='#ff0000', size='+2'))))
- log.error('Bad URL specification: %s', parts)
- elif cgidata.has_key('doit'):
- # We must be processing the list creation request
- process_request(doc, cgidata)
- elif cgidata.has_key('clear'):
- request_creation(doc)
- else:
- # Put up the list creation request form
- request_creation(doc)
- doc.AddItem('<hr>')
- # Always add the footer and print the document
- doc.AddItem(_('Return to the ') +
- Link(Utils.ScriptURL('listinfo'),
- _('general list overview')).Format())
- doc.AddItem(_('<br>Return to the ') +
- Link(Utils.ScriptURL('admin'),
- _('administrative list overview')).Format())
- doc.AddItem(MailmanLogo())
- print doc.Format()
-
-
-
-def process_request(doc, cgidata):
- # Lowercase the listname since this is treated as the 'internal' name.
- listname = cgidata.getvalue('listname', '').strip().lower()
- owner = cgidata.getvalue('owner', '').strip()
- try:
- autogen = bool(int(cgidata.getvalue('autogen', '0')))
- except ValueError:
- autogen = False
- try:
- notify = bool(int(cgidata.getvalue('notify', '0')))
- except ValueError:
- notify = False
- try:
- moderate = bool(int(cgidata.getvalue('moderate',
- config.DEFAULT_DEFAULT_MEMBER_MODERATION)))
- except ValueError:
- moderate = config.DEFAULT_DEFAULT_MEMBER_MODERATION
-
- password = cgidata.getvalue('password', '').strip()
- confirm = cgidata.getvalue('confirm', '').strip()
- auth = cgidata.getvalue('auth', '').strip()
- langs = cgidata.getvalue('langs', [config.DEFAULT_SERVER_LANGUAGE])
-
- if not isinstance(langs, list):
- langs = [langs]
- # Sanity checks
- safelistname = Utils.websafe(listname)
- if '@' in listname:
- request_creation(doc, cgidata,
- _('List name must not include "@": $safelistname'))
- return
- if not listname:
- request_creation(doc, cgidata,
- _('You forgot to enter the list name'))
- return
- if not owner:
- request_creation(doc, cgidata,
- _('You forgot to specify the list owner'))
- return
- if autogen:
- if password or confirm:
- request_creation(
- doc, cgidata,
- _("""Leave the initial password (and confirmation) fields
- blank if you want Mailman to autogenerate the list
- passwords."""))
- return
- password = confirm = Utils.MakeRandomPassword(
- config.ADMIN_PASSWORD_LENGTH)
- else:
- if password <> confirm:
- request_creation(doc, cgidata,
- _('Initial list passwords do not match'))
- return
- if not password:
- request_creation(
- doc, cgidata,
- # The little <!-- ignore --> tag is used so that this string
- # differs from the one in bin/newlist. The former is destined
- # for the web while the latter is destined for email, so they
- # must be different entries in the message catalog.
- _('The list password cannot be empty<!-- ignore -->'))
- return
- # The authorization password must be non-empty, and it must match either
- # the list creation password or the site admin password
- ok = False
- if auth:
- ok = Utils.check_global_password(auth, False)
- if not ok:
- ok = Utils.check_global_password(auth)
- if not ok:
- request_creation(
- doc, cgidata,
- _('You are not authorized to create new mailing lists'))
- return
- # Make sure the url host name matches one of our virtual domains. Then
- # calculate the list's posting address.
- url_host = Utils.get_request_domain()
- # Find the IDomain matching this url_host if there is one.
- for email_host, domain in config.domains:
- if domain.url_host == url_host:
- email_host = domain.email_host
- break
- else:
- safehostname = Utils.websafe(url_host)
- request_creation(doc, cgidata,
- _('Unknown virtual host: $safehostname'))
- return
- fqdn_listname = '%s@%s' % (listname, email_host)
- # We've got all the data we need, so go ahead and try to create the list
- mlist = MailList.MailList()
- try:
- scheme = passwords.lookup_scheme(config.PASSWORD_SCHEME.lower())
- pw = passwords.make_secret(password, scheme)
- try:
- mlist.Create(fqdn_listname, owner, pw, langs)
- except Errors.EmailAddressError, s:
- request_creation(doc, cgidata, _('Bad owner email address: $s'))
- return
- except Errors.MMListAlreadyExistsError:
- safelistname = Utils.websafe(listname)
- request_creation(doc, cgidata,
- _('List already exists: $safelistname'))
- return
- except Errors.InvalidEmailAddress, s:
- request_creation(doc, cgidata, _('Illegal list name: $s'))
- return
- except Errors.MMListError:
- request_creation(
- doc, cgidata,
- _("""Some unknown error occurred while creating the list.
- Please contact the site administrator for assistance."""))
- return
- # Initialize the host_name and web_page_url attributes, based on
- # virtual hosting settings and the request environment variables.
- mlist.default_member_moderation = moderate
- mlist.Save()
- finally:
- mlist.Unlock()
- # Now do the MTA-specific list creation tasks
- if config.MTA:
- modname = 'Mailman.MTA.' + config.MTA
- __import__(modname)
- sys.modules[modname].create(mlist, cgi=True)
- # And send the notice to the list owner.
- if notify:
- text = Utils.maketext(
- 'newlist.txt',
- {'listname' : listname,
- 'password' : password,
- 'admin_url' : mlist.GetScriptURL('admin', absolute=True),
- 'listinfo_url': mlist.GetScriptURL('listinfo', absolute=True),
- 'requestaddr' : mlist.GetRequestEmail(),
- 'siteowner' : mlist.no_reply_address,
- }, mlist=mlist)
- msg = Message.UserNotification(
- owner, mlist.no_reply_address,
- _('Your new mailing list: $listname'),
- text, mlist.preferred_language)
- msg.send(mlist)
- # Success!
- listinfo_url = mlist.GetScriptURL('listinfo')
- admin_url = mlist.GetScriptURL('admin')
- create_url = Utils.ScriptURL('create')
-
- title = _('Mailing list creation results')
- doc.SetTitle(title)
- table = Table(border=0, width='100%')
- table.AddRow([Center(Bold(FontAttr(title, size='+1')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0,
- bgcolor=config.WEB_HEADER_COLOR)
- table.AddRow([_("""You have successfully created the mailing list
- <b>$listname</b> and notification has been sent to the list owner
- <b>$owner</b>. You can now:""")])
- ullist = UnorderedList()
- ullist.AddItem(Link(listinfo_url, _("Visit the list's info page")))
- ullist.AddItem(Link(admin_url, _("Visit the list's admin page")))
- ullist.AddItem(Link(create_url, _('Create another list')))
- table.AddRow([ullist])
- doc.AddItem(table)
-
-
-
-# Because the cgi module blows
-class Dummy:
- def getvalue(self, name, default):
- return default
-dummy = Dummy()
-
-
-
-def request_creation(doc, cgidata=dummy, errmsg=None):
- # What virtual domain are we using?
- hostname = Utils.get_request_domain()
- # Set up the document
- title = _('Create a $hostname Mailing List')
- doc.SetTitle(title)
- table = Table(border=0, width='100%')
- table.AddRow([Center(Bold(FontAttr(title, size='+1')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0,
- bgcolor=config.WEB_HEADER_COLOR)
- # Add any error message
- if errmsg:
- table.AddRow([Header(3, Bold(
- FontAttr(_('Error: '), color='#ff0000', size='+2').Format() +
- Italic(errmsg).Format()))])
- table.AddRow([_("""You can create a new mailing list by entering the
- relevant information into the form below. The name of the mailing list
- will be used as the primary address for posting messages to the list, so
- it should be lowercased. You will not be able to change this once the
- list is created.
-
- <p>You also need to enter the email address of the initial list owner.
- Once the list is created, the list owner will be given notification, along
- with the initial list password. The list owner will then be able to
- modify the password and add or remove additional list owners.
-
- <p>If you want Mailman to automatically generate the initial list admin
- password, click on `Yes' in the autogenerate field below, and leave the
- initial list password fields empty.
-
- <p>You must have the proper authorization to create new mailing lists.
- Each site should have a <em>list creator's</em> password, which you can
- enter in the field at the bottom. Note that the site administrator's
- password can also be used for authentication.
- """)])
- # Build the form for the necessary input
- GREY = config.WEB_ADMINITEM_COLOR
- form = Form(Utils.ScriptURL('create'))
- ftable = Table(border=0, cols='2', width='100%',
- cellspacing=3, cellpadding=4)
-
- ftable.AddRow([Center(Italic(_('List Identity')))])
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, colspan=2)
-
- safelistname = Utils.websafe(cgidata.getvalue('listname', ''))
- ftable.AddRow([Label(_('Name of list:')),
- TextBox('listname', safelistname)])
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
-
- safeowner = Utils.websafe(cgidata.getvalue('owner', ''))
- ftable.AddRow([Label(_('Initial list owner address:')),
- TextBox('owner', safeowner)])
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
-
- try:
- autogen = bool(int(cgidata.getvalue('autogen', '0')))
- except ValueError:
- autogen = False
- ftable.AddRow([Label(_('Auto-generate initial list password?')),
- RadioButtonArray('autogen', (_('No'), _('Yes')),
- checked=autogen,
- values=(0, 1))])
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
-
- safepasswd = Utils.websafe(cgidata.getvalue('password', ''))
- ftable.AddRow([Label(_('Initial list password:')),
- PasswordBox('password', safepasswd)])
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
-
- safeconfirm = Utils.websafe(cgidata.getvalue('confirm', ''))
- ftable.AddRow([Label(_('Confirm initial password:')),
- PasswordBox('confirm', safeconfirm)])
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
-
- try:
- notify = bool(int(cgidata.getvalue('notify', '1')))
- except ValueError:
- notify = True
- try:
- moderate = bool(int(cgidata.getvalue('moderate',
- config.DEFAULT_DEFAULT_MEMBER_MODERATION)))
- except ValueError:
- moderate = config.DEFAULT_DEFAULT_MEMBER_MODERATION
-
- ftable.AddRow([Center(Italic(_('List Characteristics')))])
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, colspan=2)
-
- ftable.AddRow([
- Label(_("""Should new members be quarantined before they
- are allowed to post unmoderated to this list? Answer <em>Yes</em> to hold
- new member postings for moderator approval by default.""")),
- RadioButtonArray('moderate', (_('No'), _('Yes')),
- checked=moderate,
- values=(0,1))])
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
- # Create the table of initially supported languages, sorted on the long
- # name of the language.
- revmap = {}
- for key, (name, charset) in config.LC_DESCRIPTIONS.items():
- revmap[_(name)] = key
- langnames = revmap.keys()
- langnames.sort()
- langs = []
- for name in langnames:
- langs.append(revmap[name])
- try:
- langi = langs.index(config.DEFAULT_SERVER_LANGUAGE)
- except ValueError:
- # Someone must have deleted the servers's preferred language. Could
- # be other trouble lurking!
- langi = 0
- # BAW: we should preserve the list of checked languages across form
- # invocations.
- checked = [0] * len(langs)
- checked[langi] = 1
- deflang = _(
- config.languages.get_description(config.DEFAULT_SERVER_LANGUAGE))
- ftable.AddRow([Label(_(
- """Initial list of supported languages. <p>Note that if you do not
- select at least one initial language, the list will use the server
- default language of $deflang""")),
- CheckBoxArray('langs',
- [_(config.languges.get_description(code))
- for code in langs],
- checked=checked,
- values=langs)])
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
-
- ftable.AddRow([Label(_('Send "list created" email to list owner?')),
- RadioButtonArray('notify', (_('No'), _('Yes')),
- checked=notify,
- values=(0, 1))])
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
-
- ftable.AddRow(['<hr>'])
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, colspan=2)
- ftable.AddRow([Label(_("List creator's (authentication) password:")),
- PasswordBox('auth')])
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
-
- ftable.AddRow([Center(SubmitButton('doit', _('Create List'))),
- Center(SubmitButton('clear', _('Clear Form')))])
- form.AddItem(ftable)
- table.AddRow([form])
- doc.AddItem(table)
diff --git a/mailman/web/Cgi/edithtml.py b/mailman/web/Cgi/edithtml.py
deleted file mode 100644
index dfc871ec1..000000000
--- a/mailman/web/Cgi/edithtml.py
+++ /dev/null
@@ -1,175 +0,0 @@
-# Copyright (C) 1998-2009 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""Script which implements admin editing of the list's html templates."""
-
-import os
-import re
-import cgi
-import errno
-import logging
-
-from Mailman import Defaults
-from Mailman import Errors
-from Mailman import MailList
-from Mailman import Utils
-from Mailman import i18n
-from Mailman.Cgi import Auth
-from Mailman.HTMLFormatter import HTMLFormatter
-from Mailman.configuration import config
-from Mailman.htmlformat import *
-
-_ = i18n._
-
-log = logging.getLogger('mailman.error')
-
-
-
-def main():
- # Trick out pygettext since we want to mark template_data as translatable,
- # but we don't want to actually translate it here.
- def _(s):
- return s
-
- template_data = (
- ('listinfo.html', _('General list information page')),
- ('subscribe.html', _('Subscribe results page')),
- ('options.html', _('User specific options page')),
- ('subscribeack.txt', _('Welcome email text file')),
- )
-
- _ = i18n._
- doc = Document()
-
- # Set up the system default language
- i18n.set_language(config.DEFAULT_SERVER_LANGUAGE)
- doc.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
- parts = Utils.GetPathPieces()
- if not parts:
- doc.AddItem(Header(2, _("List name is required.")))
- print doc.Format()
- return
-
- listname = parts[0].lower()
- try:
- mlist = MailList.MailList(listname, lock=0)
- except Errors.MMListError, e:
- # Avoid cross-site scripting attacks
- safelistname = Utils.websafe(listname)
- doc.AddItem(Header(2, _('No such list <em>%(safelistname)s</em>')))
- print doc.Format()
- log.error('No such list "%s": %s', listname, e)
- return
-
- # Now that we have a valid list, set the language to its default
- i18n.set_language(mlist.preferred_language)
- doc.set_language(mlist.preferred_language)
-
- # Must be authenticated to get any farther
- cgidata = cgi.FieldStorage()
-
- # Editing the html for a list is limited to the list admin and site admin.
- if not mlist.WebAuthenticate((Defaults.AuthListAdmin,
- Defaults.AuthSiteAdmin),
- cgidata.getvalue('adminpw', '')):
- if cgidata.has_key('admlogin'):
- # This is a re-authorization attempt
- msg = Bold(FontSize('+1', _('Authorization failed.'))).Format()
- else:
- msg = ''
- Auth.loginpage(mlist, 'admin', msg=msg)
- return
-
- realname = mlist.real_name
- if len(parts) > 1:
- template_name = parts[1]
- for (template, info) in template_data:
- if template == template_name:
- template_info = _(info)
- doc.SetTitle(_(
- '%(realname)s -- Edit html for %(template_info)s'))
- break
- else:
- # Avoid cross-site scripting attacks
- safetemplatename = Utils.websafe(template_name)
- doc.SetTitle(_('Edit HTML : Error'))
- doc.AddItem(Header(2, _("%(safetemplatename)s: Invalid template")))
- doc.AddItem(mlist.GetMailmanFooter())
- print doc.Format()
- return
- else:
- doc.SetTitle(_('%(realname)s -- HTML Page Editing'))
- doc.AddItem(Header(1, _('%(realname)s -- HTML Page Editing')))
- doc.AddItem(Header(2, _('Select page to edit:')))
- template_list = UnorderedList()
- for (template, info) in template_data:
- l = Link(mlist.GetScriptURL('edithtml') + '/' + template, _(info))
- template_list.AddItem(l)
- doc.AddItem(FontSize("+2", template_list))
- doc.AddItem(mlist.GetMailmanFooter())
- print doc.Format()
- return
-
- try:
- if cgidata.keys():
- ChangeHTML(mlist, cgidata, template_name, doc)
- FormatHTML(mlist, doc, template_name, template_info)
- finally:
- doc.AddItem(mlist.GetMailmanFooter())
- print doc.Format()
-
-
-
-def FormatHTML(mlist, doc, template_name, template_info):
- doc.AddItem(Header(1,'%s:' % mlist.real_name))
- doc.AddItem(Header(1, template_info))
- doc.AddItem('<hr>')
-
- link = Link(mlist.GetScriptURL('admin'),
- _('View or edit the list configuration information.'))
-
- doc.AddItem(FontSize("+1", link))
- doc.AddItem('<p>')
- doc.AddItem('<hr>')
- form = Form(mlist.GetScriptURL('edithtml') + '/' + template_name)
- text = Utils.websafe(Utils.maketext(template_name, raw=1, mlist=mlist))
- form.AddItem(TextArea('html_code', text, rows=40, cols=75))
- form.AddItem('<p>' + _('When you are done making changes...'))
- form.AddItem(SubmitButton('submit', _('Submit Changes')))
- doc.AddItem(form)
-
-
-
-def ChangeHTML(mlist, cgi_info, template_name, doc):
- if not cgi_info.has_key('html_code'):
- doc.AddItem(Header(3,_("Can't have empty html page.")))
- doc.AddItem(Header(3,_("HTML Unchanged.")))
- doc.AddItem('<hr>')
- return
- code = cgi_info['html_code'].value
- code = re.sub(r'<([/]?script.*?)>', r'&lt;\1&gt;', code)
- langdir = os.path.join(mlist.fullpath(), mlist.preferred_language)
- # Make sure the directory exists
- Utils.makedirs(langdir)
- fp = open(os.path.join(langdir, template_name), 'w')
- try:
- fp.write(code)
- finally:
- fp.close()
- doc.AddItem(Header(3, _('HTML successfully updated.')))
- doc.AddItem('<hr>')
diff --git a/mailman/web/Cgi/listinfo.py b/mailman/web/Cgi/listinfo.py
deleted file mode 100644
index 149be715f..000000000
--- a/mailman/web/Cgi/listinfo.py
+++ /dev/null
@@ -1,207 +0,0 @@
-# Copyright (C) 1998-2009 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""Produce listinfo page, primary web entry-point to mailing lists."""
-
-# No lock needed in this script, because we don't change data.
-
-import os
-import cgi
-import logging
-
-from Mailman import Errors
-from Mailman import MailList
-from Mailman import Utils
-from Mailman import i18n
-from Mailman.configuration import config
-from Mailman.htmlformat import *
-
-# Set up i18n
-_ = i18n._
-i18n.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
-log = logging.getLogger('mailman.error')
-
-
-
-def main():
- parts = Utils.GetPathPieces()
- if not parts:
- listinfo_overview()
- return
-
- listname = parts[0].lower()
- try:
- mlist = MailList.MailList(listname, lock=0)
- except Errors.MMListError, e:
- # Avoid cross-site scripting attacks
- safelistname = Utils.websafe(listname)
- listinfo_overview(_('No such list <em>%(safelistname)s</em>'))
- log.error('No such list "%s": %s', listname, e)
- return
-
- # See if the user want to see this page in other language
- cgidata = cgi.FieldStorage()
- language = cgidata.getvalue('language')
- if language not in config.languages.enabled_codes:
- language = mlist.preferred_language
- i18n.set_language(language)
- list_listinfo(mlist, language)
-
-
-
-def listinfo_overview(msg=''):
- # Present the general listinfo overview
- hostname = Utils.get_request_domain()
- # Set up the document and assign it the correct language. The only one we
- # know about at the moment is the server's default.
- doc = Document()
- doc.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
- legend = _("%(hostname)s Mailing Lists")
- doc.SetTitle(legend)
-
- table = Table(border=0, width="100%")
- table.AddRow([Center(Header(2, legend))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2,
- bgcolor=config.WEB_HEADER_COLOR)
-
- # Skip any mailing lists that isn't advertised.
- advertised = []
- for name in sorted(config.list_manager.names):
- mlist = MailList.MailList(name, lock=False)
- if mlist.advertised:
- if hostname not in mlist.web_page_url:
- # This list is situated in a different virtual domain
- continue
- else:
- advertised.append((mlist.GetScriptURL('listinfo'),
- mlist.real_name,
- mlist.description))
- if msg:
- greeting = FontAttr(msg, color="ff5060", size="+1")
- else:
- greeting = FontAttr(_('Welcome!'), size='+2')
-
- welcome = [greeting]
- mailmanlink = Link(config.MAILMAN_URL, _('Mailman')).Format()
- if not advertised:
- welcome.extend(
- _('''<p>There currently are no publicly-advertised
- %(mailmanlink)s mailing lists on %(hostname)s.'''))
- else:
- welcome.append(
- _('''<p>Below is a listing of all the public mailing lists on
- %(hostname)s. Click on a list name to get more information about
- the list, or to subscribe, unsubscribe, and change the preferences
- on your subscription.'''))
-
- # set up some local variables
- adj = msg and _('right') or ''
- siteowner = Utils.get_site_noreply()
- welcome.extend(
- (_(''' To visit the general information page for an unadvertised list,
- open a URL similar to this one, but with a '/' and the %(adj)s
- list name appended.
- <p>List administrators, you can visit '''),
- Link(Utils.ScriptURL('admin'),
- _('the list admin overview page')),
- _(''' to find the management interface for your list.
- <p>If you are having trouble using the lists, please contact '''),
- Link('mailto:' + siteowner, siteowner),
- '.<p>'))
-
- table.AddRow([apply(Container, welcome)])
- table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 0, colspan=2)
-
- if advertised:
- table.AddRow(['&nbsp;', '&nbsp;'])
- table.AddRow([Bold(FontAttr(_('List'), size='+2')),
- Bold(FontAttr(_('Description'), size='+2'))
- ])
- highlight = 1
- for url, real_name, description in advertised:
- table.AddRow(
- [Link(url, Bold(real_name)),
- description or Italic(_('[no description available]'))])
- if highlight and config.WEB_HIGHLIGHT_COLOR:
- table.AddRowInfo(table.GetCurrentRowIndex(),
- bgcolor=config.WEB_HIGHLIGHT_COLOR)
- highlight = not highlight
-
- doc.AddItem(table)
- doc.AddItem('<hr>')
- doc.AddItem(MailmanLogo())
- print doc.Format()
-
-
-
-def list_listinfo(mlist, lang):
- # Generate list specific listinfo
- doc = HeadlessDocument()
- doc.set_language(lang)
-
- replacements = mlist.GetStandardReplacements(lang)
-
- if not mlist.digestable or not mlist.nondigestable:
- replacements['<mm-digest-radio-button>'] = ""
- replacements['<mm-undigest-radio-button>'] = ""
- replacements['<mm-digest-question-start>'] = '<!-- '
- replacements['<mm-digest-question-end>'] = ' -->'
- else:
- replacements['<mm-digest-radio-button>'] = mlist.FormatDigestButton()
- replacements['<mm-undigest-radio-button>'] = \
- mlist.FormatUndigestButton()
- replacements['<mm-digest-question-start>'] = ''
- replacements['<mm-digest-question-end>'] = ''
- replacements['<mm-plain-digests-button>'] = \
- mlist.FormatPlainDigestsButton()
- replacements['<mm-mime-digests-button>'] = mlist.FormatMimeDigestsButton()
- replacements['<mm-subscribe-box>'] = mlist.FormatBox('email', size=30)
- replacements['<mm-subscribe-button>'] = mlist.FormatButton(
- 'email-button', text=_('Subscribe'))
- replacements['<mm-new-password-box>'] = mlist.FormatSecureBox('pw')
- replacements['<mm-confirm-password>'] = mlist.FormatSecureBox('pw-conf')
- replacements['<mm-subscribe-form-start>'] = mlist.FormatFormStart(
- 'subscribe')
- # Roster form substitutions
- replacements['<mm-roster-form-start>'] = mlist.FormatFormStart('roster')
- replacements['<mm-roster-option>'] = mlist.FormatRosterOptionForUser(lang)
- # Options form substitutions
- replacements['<mm-options-form-start>'] = mlist.FormatFormStart('options')
- replacements['<mm-editing-options>'] = mlist.FormatEditingOption(lang)
- replacements['<mm-info-button>'] = SubmitButton('UserOptions',
- _('Edit Options')).Format()
- # If only one language is enabled for this mailing list, omit the choice
- # buttons.
- if len(mlist.language_codes) == 1:
- displang = ''
- else:
- displang = mlist.FormatButton('displang-button',
- text = _("View this page in"))
- replacements['<mm-displang-box>'] = displang
- replacements['<mm-lang-form-start>'] = mlist.FormatFormStart('listinfo')
- replacements['<mm-fullname-box>'] = mlist.FormatBox('fullname', size=30)
-
- # Do the expansion.
- doc.AddItem(mlist.ParseTags('listinfo.html', replacements, lang))
- print doc.Format()
-
-
-
-if __name__ == "__main__":
- main()
diff --git a/mailman/web/Cgi/options.py b/mailman/web/Cgi/options.py
deleted file mode 100644
index c529a3ea4..000000000
--- a/mailman/web/Cgi/options.py
+++ /dev/null
@@ -1,1000 +0,0 @@
-# Copyright (C) 1998-2009 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""Produce and handle the member options."""
-
-import os
-import cgi
-import sys
-import urllib
-import logging
-
-from Mailman import Errors
-from Mailman import MailList
-from Mailman import MemberAdaptor
-from Mailman import Utils
-from Mailman import i18n
-from Mailman import passwords
-from Mailman.configuration import config
-from Mailman.htmlformat import *
-
-
-OR = '|'
-SLASH = '/'
-SETLANGUAGE = -1
-
-# Set up i18n
-_ = i18n._
-i18n.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
-log = logging.getLogger('mailman.error')
-mlog = logging.getLogger('mailman.mischief')
-
-
-
-def main():
- doc = Document()
- doc.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
- parts = Utils.GetPathPieces()
- lenparts = parts and len(parts)
- if not parts or lenparts < 1:
- title = _('CGI script error')
- doc.SetTitle(title)
- doc.AddItem(Header(2, title))
- doc.addError(_('Invalid options to CGI script.'))
- doc.AddItem('<hr>')
- doc.AddItem(MailmanLogo())
- print doc.Format()
- return
-
- # get the list and user's name
- listname = parts[0].lower()
- # open list
- try:
- mlist = MailList.MailList(listname, lock=0)
- except Errors.MMListError, e:
- # Avoid cross-site scripting attacks
- safelistname = Utils.websafe(listname)
- title = _('CGI script error')
- doc.SetTitle(title)
- doc.AddItem(Header(2, title))
- doc.addError(_('No such list <em>%(safelistname)s</em>'))
- doc.AddItem('<hr>')
- doc.AddItem(MailmanLogo())
- print doc.Format()
- log.error('No such list "%s": %s\n', listname, e)
- return
-
- # The total contents of the user's response
- cgidata = cgi.FieldStorage(keep_blank_values=1)
-
- # Set the language for the page. If we're coming from the listinfo cgi,
- # we might have a 'language' key in the cgi data. That was an explicit
- # preference to view the page in, so we should honor that here. If that's
- # not available, use the list's default language.
- language = cgidata.getvalue('language')
- if language not in config.languages.enabled_codes:
- language = mlist.preferred_language
- i18n.set_language(language)
- doc.set_language(language)
-
- if lenparts < 2:
- user = cgidata.getvalue('email')
- if not user:
- # If we're coming from the listinfo page and we left the email
- # address field blank, it's not an error. listinfo.html names the
- # button UserOptions; we can use that as the descriminator.
- if not cgidata.getvalue('UserOptions'):
- doc.addError(_('No address given'))
- loginpage(mlist, doc, None, language)
- print doc.Format()
- return
- else:
- user = Utils.LCDomain(Utils.UnobscureEmail(SLASH.join(parts[1:])))
-
- # Avoid cross-site scripting attacks
- safeuser = Utils.websafe(user)
- try:
- Utils.ValidateEmail(user)
- except Errors.EmailAddressError:
- doc.addError(_('Illegal Email Address: %(safeuser)s'))
- loginpage(mlist, doc, None, language)
- print doc.Format()
- return
- # Sanity check the user, but only give the "no such member" error when
- # using public rosters, otherwise, we'll leak membership information.
- if not mlist.isMember(user) and mlist.private_roster == 0:
- doc.addError(_('No such member: %(safeuser)s.'))
- loginpage(mlist, doc, None, language)
- print doc.Format()
- return
-
- # Find the case preserved email address (the one the user subscribed with)
- lcuser = user.lower()
- try:
- cpuser = mlist.getMemberCPAddress(lcuser)
- except Errors.NotAMemberError:
- # This happens if the user isn't a member but we've got private rosters
- cpuser = None
- if lcuser == cpuser:
- cpuser = None
-
- # And now we know the user making the request, so set things up to for the
- # user's stored preferred language, overridden by any form settings for
- # their new language preference.
- userlang = cgidata.getvalue('language')
- if userlang not in config.languages.enabled_codes:
- userlang = mlist.getMemberLanguage(user)
- doc.set_language(userlang)
- i18n.set_language(userlang)
-
- # See if this is VARHELP on topics.
- varhelp = None
- if cgidata.has_key('VARHELP'):
- varhelp = cgidata['VARHELP'].value
- elif os.environ.get('QUERY_STRING'):
- # POST methods, even if their actions have a query string, don't get
- # put into FieldStorage's keys :-(
- qs = cgi.parse_qs(os.environ['QUERY_STRING']).get('VARHELP')
- if qs and isinstance(qs, list):
- varhelp = qs[0]
- if varhelp:
- topic_details(mlist, doc, user, cpuser, userlang, varhelp)
- return
-
- # Are we processing an unsubscription request from the login screen?
- if cgidata.has_key('login-unsub'):
- # Because they can't supply a password for unsubscribing, we'll need
- # to do the confirmation dance.
- if mlist.isMember(user):
- # We must acquire the list lock in order to pend a request.
- try:
- mlist.Lock()
- # If unsubs require admin approval, then this request has to
- # be held. Otherwise, send a confirmation.
- if mlist.unsubscribe_policy:
- mlist.HoldUnsubscription(user)
- doc.addError(_("""Your unsubscription request has been
- forwarded to the list administrator for approval."""),
- tag='')
- else:
- mlist.ConfirmUnsubscription(user, userlang)
- doc.addError(_('The confirmation email has been sent.'),
- tag='')
- mlist.Save()
- finally:
- mlist.Unlock()
- else:
- # Not a member
- if mlist.private_roster == 0:
- # Public rosters
- doc.addError(_('No such member: %(safeuser)s.'))
- else:
- mlog.error('Unsub attempt of non-member w/ private rosters: %s',
- user)
- doc.addError(_('The confirmation email has been sent.'),
- tag='')
- loginpage(mlist, doc, user, language)
- print doc.Format()
- return
-
- # Are we processing a password reminder from the login screen?
- if cgidata.has_key('login-remind'):
- if mlist.isMember(user):
- mlist.MailUserPassword(user)
- doc.addError(
- _('A reminder of your password has been emailed to you.'),
- tag='')
- else:
- # Not a member
- if mlist.private_roster == 0:
- # Public rosters
- doc.addError(_('No such member: %(safeuser)s.'))
- else:
- mlog.error(
- 'Reminder attempt of non-member w/ private rosters: %s',
- user)
- doc.addError(
- _('A reminder of your password has been emailed to you.'),
- tag='')
- loginpage(mlist, doc, user, language)
- print doc.Format()
- return
-
- # Get the password from the form.
- password = cgidata.getvalue('password', '').strip()
- # Check authentication. We need to know if the credentials match the user
- # or the site admin, because they are the only ones who are allowed to
- # change things globally. Specifically, the list admin may not change
- # values globally.
- if config.ALLOW_SITE_ADMIN_COOKIES:
- user_or_siteadmin_context = (config.AuthUser, config.AuthSiteAdmin)
- else:
- # Site and list admins are treated equal so that list admin can pass
- # site admin test. :-(
- user_or_siteadmin_context = (config.AuthUser,)
- is_user_or_siteadmin = mlist.WebAuthenticate(
- user_or_siteadmin_context, password, user)
- # Authenticate, possibly using the password supplied in the login page
- if not is_user_or_siteadmin and \
- not mlist.WebAuthenticate((config.AuthListAdmin,
- config.AuthSiteAdmin),
- password, user):
- # Not authenticated, so throw up the login page again. If they tried
- # to authenticate via cgi (instead of cookie), then print an error
- # message.
- if cgidata.has_key('password'):
- doc.addError(_('Authentication failed.'))
- # So as not to allow membership leakage, prompt for the email
- # address and the password here.
- if mlist.private_roster <> 0:
- mlog.error('Login failure with private rosters: %s', user)
- user = None
- loginpage(mlist, doc, user, language)
- print doc.Format()
- return
-
- # From here on out, the user is okay to view and modify their membership
- # options. The first set of checks does not require the list to be
- # locked.
-
- if cgidata.has_key('logout'):
- print mlist.ZapCookie(config.AuthUser, user)
- loginpage(mlist, doc, user, language)
- print doc.Format()
- return
-
- if cgidata.has_key('emailpw'):
- mlist.MailUserPassword(user)
- options_page(
- mlist, doc, user, cpuser, userlang,
- _('A reminder of your password has been emailed to you.'))
- print doc.Format()
- return
-
- if cgidata.has_key('othersubs'):
- # Only the user or site administrator can view all subscriptions.
- if not is_user_or_siteadmin:
- doc.addError(_("""The list administrator may not view the other
- subscriptions for this user."""), _('Note: '))
- options_page(mlist, doc, user, cpuser, userlang)
- print doc.Format()
- return
- hostname = mlist.host_name
- title = _('List subscriptions for %(safeuser)s on %(hostname)s')
- doc.SetTitle(title)
- doc.AddItem(Header(2, title))
- doc.AddItem(_('''Click on a link to visit your options page for the
- requested mailing list.'''))
-
- # Troll through all the mailing lists that match host_name and see if
- # the user is a member. If so, add it to the list.
- onlists = []
- for gmlist in lists_of_member(mlist, user) + [mlist]:
- url = gmlist.GetOptionsURL(user)
- link = Link(url, gmlist.real_name)
- onlists.append((gmlist.real_name, link))
- onlists.sort()
- items = OrderedList(*[link for name, link in onlists])
- doc.AddItem(items)
- print doc.Format()
- return
-
- if cgidata.has_key('change-of-address'):
- # We could be changing the user's full name, email address, or both.
- # Watch out for non-ASCII characters in the member's name.
- membername = cgidata.getvalue('fullname')
- # Canonicalize the member's name
- membername = Utils.canonstr(membername, language)
- newaddr = cgidata.getvalue('new-address')
- confirmaddr = cgidata.getvalue('confirm-address')
-
- oldname = mlist.getMemberName(user)
- set_address = set_membername = 0
-
- # See if the user wants to change their email address globally. The
- # list admin is /not/ allowed to make global changes.
- globally = cgidata.getvalue('changeaddr-globally')
- if globally and not is_user_or_siteadmin:
- doc.addError(_("""The list administrator may not change the names
- or addresses for this user's other subscriptions. However, the
- subscription for this mailing list has been changed."""),
- _('Note: '))
- globally = False
- # We will change the member's name under the following conditions:
- # - membername has a value
- # - membername has no value, but they /used/ to have a membername
- if membername and membername <> oldname:
- # Setting it to a new value
- set_membername = 1
- if not membername and oldname:
- # Unsetting it
- set_membername = 1
- # We will change the user's address if both newaddr and confirmaddr
- # are non-blank, have the same value, and aren't the currently
- # subscribed email address (when compared case-sensitively). If both
- # are blank, but membername is set, we ignore it, otherwise we print
- # an error.
- msg = ''
- if newaddr and confirmaddr:
- if newaddr <> confirmaddr:
- options_page(mlist, doc, user, cpuser, userlang,
- _('Addresses did not match!'))
- print doc.Format()
- return
- if newaddr == cpuser:
- options_page(mlist, doc, user, cpuser, userlang,
- _('You are already using that email address'))
- print doc.Format()
- return
- # If they're requesting to subscribe an address which is already a
- # member, and they're /not/ doing it globally, then refuse.
- # Otherwise, we'll agree to do it globally (with a warning
- # message) and let ApprovedChangeMemberAddress() handle already a
- # member issues.
- if mlist.isMember(newaddr):
- safenewaddr = Utils.websafe(newaddr)
- if globally:
- listname = mlist.real_name
- msg += _("""\
-The new address you requested %(newaddr)s is already a member of the
-%(listname)s mailing list, however you have also requested a global change of
-address. Upon confirmation, any other mailing list containing the address
-%(safeuser)s will be changed. """)
- # Don't return
- else:
- options_page(
- mlist, doc, user, cpuser, userlang,
- _('The new address is already a member: %(newaddr)s'))
- print doc.Format()
- return
- set_address = 1
- elif (newaddr or confirmaddr) and not set_membername:
- options_page(mlist, doc, user, cpuser, userlang,
- _('Addresses may not be blank'))
- print doc.Format()
- return
- if set_address:
- if cpuser is None:
- cpuser = user
- # Register the pending change after the list is locked
- msg += _('A confirmation message has been sent to %(newaddr)s. ')
- mlist.Lock()
- try:
- try:
- mlist.ChangeMemberAddress(cpuser, newaddr, globally)
- mlist.Save()
- finally:
- mlist.Unlock()
- except Errors.InvalidEmailAddress:
- msg = _('Invalid email address provided')
- except Errors.MMAlreadyAMember:
- msg = _('%(newaddr)s is already a member of the list.')
- except Errors.MembershipIsBanned:
- owneraddr = mlist.GetOwnerEmail()
- msg = _("""%(newaddr)s is banned from this list. If you
- think this restriction is erroneous, please contact
- the list owners at %(owneraddr)s.""")
-
- if set_membername:
- mlist.Lock()
- try:
- mlist.ChangeMemberName(user, membername, globally)
- mlist.Save()
- finally:
- mlist.Unlock()
- msg += _('Member name successfully changed. ')
-
- options_page(mlist, doc, user, cpuser, userlang, msg)
- print doc.Format()
- return
-
- if cgidata.has_key('changepw'):
- newpw = cgidata.getvalue('newpw')
- confirmpw = cgidata.getvalue('confpw')
- if not newpw or not confirmpw:
- options_page(mlist, doc, user, cpuser, userlang,
- _('Passwords may not be blank'))
- print doc.Format()
- return
- if newpw <> confirmpw:
- options_page(mlist, doc, user, cpuser, userlang,
- _('Passwords did not match!'))
- print doc.Format()
- return
-
- # See if the user wants to change their passwords globally, however
- # the list admin is /not/ allowed to change passwords globally.
- pw_globally = cgidata.getvalue('pw-globally')
- if pw_globally and not is_user_or_siteadmin:
- doc.addError(_("""The list administrator may not change the
- password for this user's other subscriptions. However, the
- password for this mailing list has been changed."""),
- _('Note: '))
- pw_globally = False
-
- mlists = [mlist]
-
- if pw_globally:
- mlists.extend(lists_of_member(mlist, user))
-
- pw = passwords.make_secret(newpw, config.PASSWORD_SCHEME)
- for gmlist in mlists:
- change_password(gmlist, user, pw)
-
- # Regenerate the cookie so a re-authorization isn't necessary
- print mlist.MakeCookie(config.AuthUser, user)
- options_page(mlist, doc, user, cpuser, userlang,
- _('Password successfully changed.'))
- print doc.Format()
- return
-
- if cgidata.has_key('unsub'):
- # Was the confirming check box turned on?
- if not cgidata.getvalue('unsubconfirm'):
- options_page(
- mlist, doc, user, cpuser, userlang,
- _('''You must confirm your unsubscription request by turning
- on the checkbox below the <em>Unsubscribe</em> button. You
- have not been unsubscribed!'''))
- print doc.Format()
- return
-
- mlist.Lock()
- needapproval = False
- try:
- try:
- mlist.DeleteMember(
- user, 'via the member options page', userack=1)
- except Errors.MMNeedApproval:
- needapproval = True
- mlist.Save()
- finally:
- mlist.Unlock()
- # Now throw up some results page, with appropriate links. We can't
- # drop them back into their options page, because that's gone now!
- fqdn_listname = mlist.GetListEmail()
- owneraddr = mlist.GetOwnerEmail()
- url = mlist.GetScriptURL('listinfo')
-
- title = _('Unsubscription results')
- doc.SetTitle(title)
- doc.AddItem(Header(2, title))
- if needapproval:
- doc.AddItem(_("""Your unsubscription request has been received and
- forwarded on to the list moderators for approval. You will
- receive notification once the list moderators have made their
- decision."""))
- else:
- doc.AddItem(_("""You have been successfully unsubscribed from the
- mailing list %(fqdn_listname)s. If you were receiving digest
- deliveries you may get one more digest. If you have any questions
- about your unsubscription, please contact the list owners at
- %(owneraddr)s."""))
- doc.AddItem(mlist.GetMailmanFooter())
- print doc.Format()
- return
-
- if cgidata.has_key('options-submit'):
- # Digest action flags
- digestwarn = 0
- cantdigest = 0
- mustdigest = 0
-
- newvals = []
- # First figure out which options have changed. The item names come
- # from FormatOptionButton() in HTMLFormatter.py
- for item, flag in (('digest', config.Digests),
- ('mime', config.DisableMime),
- ('dontreceive', config.DontReceiveOwnPosts),
- ('ackposts', config.AcknowledgePosts),
- ('disablemail', config.DisableDelivery),
- ('conceal', config.ConcealSubscription),
- ('remind', config.SuppressPasswordReminder),
- ('rcvtopic', config.ReceiveNonmatchingTopics),
- ('nodupes', config.DontReceiveDuplicates),
- ):
- try:
- newval = int(cgidata.getvalue(item))
- except (TypeError, ValueError):
- newval = None
-
- # Skip this option if there was a problem or it wasn't changed.
- # Note that delivery status is handled separate from the options
- # flags.
- if newval is None:
- continue
- elif flag == config.DisableDelivery:
- status = mlist.getDeliveryStatus(user)
- # Here, newval == 0 means enable, newval == 1 means disable
- if not newval and status <> MemberAdaptor.ENABLED:
- newval = MemberAdaptor.ENABLED
- elif newval and status == MemberAdaptor.ENABLED:
- newval = MemberAdaptor.BYUSER
- else:
- continue
- elif newval == mlist.getMemberOption(user, flag):
- continue
- # Should we warn about one more digest?
- if flag == config.Digests and \
- newval == 0 and mlist.getMemberOption(user, flag):
- digestwarn = 1
-
- newvals.append((flag, newval))
-
- # The user language is handled a little differently
- if userlang not in mlist.language_codes:
- newvals.append((SETLANGUAGE, mlist.preferred_language))
- else:
- newvals.append((SETLANGUAGE, userlang))
-
- # Process user selected topics, but don't make the changes to the
- # MailList object; we must do that down below when the list is
- # locked.
- topicnames = cgidata.getvalue('usertopic')
- if topicnames:
- # Some topics were selected. topicnames can actually be a string
- # or a list of strings depending on whether more than one topic
- # was selected or not.
- if not isinstance(topicnames, list):
- # Assume it was a bare string, so listify it
- topicnames = [topicnames]
- # unquote the topic names
- topicnames = [urllib.unquote_plus(n) for n in topicnames]
-
- # The standard sigterm handler (see above)
- def sigterm_handler(signum, frame, mlist=mlist):
- mlist.Unlock()
- sys.exit(0)
-
- # Now, lock the list and perform the changes
- mlist.Lock()
- try:
- # `values' is a tuple of flags and the web values
- for flag, newval in newvals:
- # Handle language settings differently
- if flag == SETLANGUAGE:
- mlist.setMemberLanguage(user, newval)
- # Handle delivery status separately
- elif flag == config.DisableDelivery:
- mlist.setDeliveryStatus(user, newval)
- else:
- try:
- mlist.setMemberOption(user, flag, newval)
- except Errors.CantDigestError:
- cantdigest = 1
- except Errors.MustDigestError:
- mustdigest = 1
- # Set the topics information.
- mlist.setMemberTopics(user, topicnames)
- mlist.Save()
- finally:
- mlist.Unlock()
-
- # A bag of attributes for the global options
- class Global:
- enable = None
- remind = None
- nodupes = None
- mime = None
- def __nonzero__(self):
- return len(self.__dict__.keys()) > 0
-
- globalopts = Global()
-
- # The enable/disable option and the password remind option may have
- # their global flags sets.
- if cgidata.getvalue('deliver-globally'):
- # Yes, this is inefficient, but the list is so small it shouldn't
- # make much of a difference.
- for flag, newval in newvals:
- if flag == config.DisableDelivery:
- globalopts.enable = newval
- break
-
- if cgidata.getvalue('remind-globally'):
- for flag, newval in newvals:
- if flag == config.SuppressPasswordReminder:
- globalopts.remind = newval
- break
-
- if cgidata.getvalue('nodupes-globally'):
- for flag, newval in newvals:
- if flag == config.DontReceiveDuplicates:
- globalopts.nodupes = newval
- break
-
- if cgidata.getvalue('mime-globally'):
- for flag, newval in newvals:
- if flag == config.DisableMime:
- globalopts.mime = newval
- break
-
- # Change options globally, but only if this is the user or site admin,
- # /not/ if this is the list admin.
- if globalopts:
- if not is_user_or_siteadmin:
- doc.addError(_("""The list administrator may not change the
- options for this user's other subscriptions. However the
- options for this mailing list subscription has been
- changed."""), _('Note: '))
- else:
- for gmlist in lists_of_member(mlist, user):
- global_options(gmlist, user, globalopts)
-
- # Now print the results
- if cantdigest:
- msg = _('''The list administrator has disabled digest delivery for
- this list, so your delivery option has not been set. However your
- other options have been set successfully.''')
- elif mustdigest:
- msg = _('''The list administrator has disabled non-digest delivery
- for this list, so your delivery option has not been set. However
- your other options have been set successfully.''')
- else:
- msg = _('You have successfully set your options.')
-
- if digestwarn:
- msg += _('You may get one last digest.')
-
- options_page(mlist, doc, user, cpuser, userlang, msg)
- print doc.Format()
- return
-
- if mlist.isMember(user):
- options_page(mlist, doc, user, cpuser, userlang)
- else:
- loginpage(mlist, doc, user, userlang)
- print doc.Format()
-
-
-
-def options_page(mlist, doc, user, cpuser, userlang, message=''):
- # The bulk of the document will come from the options.html template, which
- # includes it's own html armor (head tags, etc.). Suppress the head that
- # Document() derived pages get automatically.
- doc.suppress_head = 1
-
- if mlist.obscure_addresses:
- presentable_user = Utils.ObscureEmail(user, for_text=1)
- if cpuser is not None:
- cpuser = Utils.ObscureEmail(cpuser, for_text=1)
- else:
- presentable_user = user
-
- fullname = Utils.uncanonstr(mlist.getMemberName(user), userlang)
- if fullname:
- presentable_user += ', %s' % fullname
-
- # Do replacements
- replacements = mlist.GetStandardReplacements(userlang)
- replacements['<mm-results>'] = Bold(FontSize('+1', message)).Format()
- replacements['<mm-digest-radio-button>'] = mlist.FormatOptionButton(
- config.Digests, 1, user)
- replacements['<mm-undigest-radio-button>'] = mlist.FormatOptionButton(
- config.Digests, 0, user)
- replacements['<mm-plain-digests-button>'] = mlist.FormatOptionButton(
- config.DisableMime, 1, user)
- replacements['<mm-mime-digests-button>'] = mlist.FormatOptionButton(
- config.DisableMime, 0, user)
- replacements['<mm-global-mime-button>'] = (
- CheckBox('mime-globally', 1, checked=0).Format())
- replacements['<mm-delivery-enable-button>'] = mlist.FormatOptionButton(
- config.DisableDelivery, 0, user)
- replacements['<mm-delivery-disable-button>'] = mlist.FormatOptionButton(
- config.DisableDelivery, 1, user)
- replacements['<mm-disabled-notice>'] = mlist.FormatDisabledNotice(user)
- replacements['<mm-dont-ack-posts-button>'] = mlist.FormatOptionButton(
- config.AcknowledgePosts, 0, user)
- replacements['<mm-ack-posts-button>'] = mlist.FormatOptionButton(
- config.AcknowledgePosts, 1, user)
- replacements['<mm-receive-own-mail-button>'] = mlist.FormatOptionButton(
- config.DontReceiveOwnPosts, 0, user)
- replacements['<mm-dont-receive-own-mail-button>'] = (
- mlist.FormatOptionButton(config.DontReceiveOwnPosts, 1, user))
- replacements['<mm-dont-get-password-reminder-button>'] = (
- mlist.FormatOptionButton(config.SuppressPasswordReminder, 1, user))
- replacements['<mm-get-password-reminder-button>'] = (
- mlist.FormatOptionButton(config.SuppressPasswordReminder, 0, user))
- replacements['<mm-public-subscription-button>'] = (
- mlist.FormatOptionButton(config.ConcealSubscription, 0, user))
- replacements['<mm-hide-subscription-button>'] = mlist.FormatOptionButton(
- config.ConcealSubscription, 1, user)
- replacements['<mm-dont-receive-duplicates-button>'] = (
- mlist.FormatOptionButton(config.DontReceiveDuplicates, 1, user))
- replacements['<mm-receive-duplicates-button>'] = (
- mlist.FormatOptionButton(config.DontReceiveDuplicates, 0, user))
- replacements['<mm-unsubscribe-button>'] = (
- mlist.FormatButton('unsub', _('Unsubscribe')) + '<br>' +
- CheckBox('unsubconfirm', 1, checked=0).Format() +
- _('<em>Yes, I really want to unsubscribe</em>'))
- replacements['<mm-new-pass-box>'] = mlist.FormatSecureBox('newpw')
- replacements['<mm-confirm-pass-box>'] = mlist.FormatSecureBox('confpw')
- replacements['<mm-change-pass-button>'] = (
- mlist.FormatButton('changepw', _("Change My Password")))
- replacements['<mm-other-subscriptions-submit>'] = (
- mlist.FormatButton('othersubs',
- _('List my other subscriptions')))
- replacements['<mm-form-start>'] = (
- mlist.FormatFormStart('options', user))
- replacements['<mm-user>'] = user
- replacements['<mm-presentable-user>'] = presentable_user
- replacements['<mm-email-my-pw>'] = mlist.FormatButton(
- 'emailpw', (_('Email My Password To Me')))
- replacements['<mm-umbrella-notice>'] = (
- mlist.FormatUmbrellaNotice(user, _("password")))
- replacements['<mm-logout-button>'] = (
- mlist.FormatButton('logout', _('Log out')))
- replacements['<mm-options-submit-button>'] = mlist.FormatButton(
- 'options-submit', _('Submit My Changes'))
- replacements['<mm-global-pw-changes-button>'] = (
- CheckBox('pw-globally', 1, checked=0).Format())
- replacements['<mm-global-deliver-button>'] = (
- CheckBox('deliver-globally', 1, checked=0).Format())
- replacements['<mm-global-remind-button>'] = (
- CheckBox('remind-globally', 1, checked=0).Format())
- replacements['<mm-global-nodupes-button>'] = (
- CheckBox('nodupes-globally', 1, checked=0).Format())
-
- days = int(config.PENDING_REQUEST_LIFE / config.days(1))
- if days > 1:
- units = _('days')
- else:
- units = _('day')
- replacements['<mm-pending-days>'] = _('%(days)d %(units)s')
-
- replacements['<mm-new-address-box>'] = mlist.FormatBox('new-address')
- replacements['<mm-confirm-address-box>'] = mlist.FormatBox(
- 'confirm-address')
- replacements['<mm-change-address-button>'] = mlist.FormatButton(
- 'change-of-address', _('Change My Address and Name'))
- replacements['<mm-global-change-of-address>'] = CheckBox(
- 'changeaddr-globally', 1, checked=0).Format()
- replacements['<mm-fullname-box>'] = mlist.FormatBox(
- 'fullname', value=fullname)
-
- # Create the topics radios. BAW: what if the list admin deletes a topic,
- # but the user still wants to get that topic message?
- usertopics = mlist.getMemberTopics(user)
- if mlist.topics:
- table = Table(border="0")
- for name, pattern, description, emptyflag in mlist.topics:
- if emptyflag:
- continue
- quotedname = urllib.quote_plus(name)
- details = Link(mlist.GetScriptURL('options') +
- '/%s/?VARHELP=%s' % (user, quotedname),
- ' (Details)')
- if name in usertopics:
- checked = 1
- else:
- checked = 0
- table.AddRow([CheckBox('usertopic', quotedname, checked=checked),
- name + details.Format()])
- topicsfield = table.Format()
- else:
- topicsfield = _('<em>No topics defined</em>')
- replacements['<mm-topics>'] = topicsfield
- replacements['<mm-suppress-nonmatching-topics>'] = (
- mlist.FormatOptionButton(config.ReceiveNonmatchingTopics, 0, user))
- replacements['<mm-receive-nonmatching-topics>'] = (
- mlist.FormatOptionButton(config.ReceiveNonmatchingTopics, 1, user))
-
- if cpuser is not None:
- replacements['<mm-case-preserved-user>'] = _('''
-You are subscribed to this list with the case-preserved address
-<em>%(cpuser)s</em>.''')
- else:
- replacements['<mm-case-preserved-user>'] = ''
-
- doc.AddItem(mlist.ParseTags('options.html', replacements, userlang))
-
-
-
-def loginpage(mlist, doc, user, lang):
- realname = mlist.real_name
- actionurl = mlist.GetScriptURL('options')
- if user is None:
- title = _('%(realname)s list: member options login page')
- extra = _('email address and ')
- else:
- safeuser = Utils.websafe(user)
- title = _('%(realname)s list: member options for user %(safeuser)s')
- obuser = Utils.ObscureEmail(user)
- extra = ''
- # Set up the title
- doc.SetTitle(title)
- # We use a subtable here so we can put a language selection box in
- table = Table(width='100%', border=0, cellspacing=4, cellpadding=5)
- # If only one language is enabled for this mailing list, omit the choice
- # buttons.
- table.AddRow([Center(Header(2, title))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0,
- bgcolor=config.WEB_HEADER_COLOR)
- if len(mlist.language_codes) > 1:
- langform = Form(actionurl)
- langform.AddItem(SubmitButton('displang-button',
- _('View this page in')))
- langform.AddItem(mlist.GetLangSelectBox(lang))
- if user:
- langform.AddItem(Hidden('email', user))
- table.AddRow([Center(langform)])
- doc.AddItem(table)
- # Preamble
- # Set up the login page
- form = Form(actionurl)
- table = Table(width='100%', border=0, cellspacing=4, cellpadding=5)
- table.AddRow([_("""In order to change your membership option, you must
- first log in by giving your %(extra)smembership password in the section
- below. If you don't remember your membership password, you can have it
- emailed to you by clicking on the button below. If you just want to
- unsubscribe from this list, click on the <em>Unsubscribe</em> button and a
- confirmation message will be sent to you.
-
- <p><strong><em>Important:</em></strong> From this point on, you must have
- cookies enabled in your browser, otherwise none of your changes will take
- effect.
- """)])
- # Password and login button
- ptable = Table(width='50%', border=0, cellspacing=4, cellpadding=5)
- if user is None:
- ptable.AddRow([Label(_('Email address:')),
- TextBox('email', size=20)])
- else:
- ptable.AddRow([Hidden('email', user)])
- ptable.AddRow([Label(_('Password:')),
- PasswordBox('password', size=20)])
- ptable.AddRow([Center(SubmitButton('login', _('Log in')))])
- ptable.AddCellInfo(ptable.GetCurrentRowIndex(), 0, colspan=2)
- table.AddRow([Center(ptable)])
- # Unsubscribe section
- table.AddRow([Center(Header(2, _('Unsubscribe')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0,
- bgcolor=config.WEB_HEADER_COLOR)
-
- table.AddRow([_("""By clicking on the <em>Unsubscribe</em> button, a
- confirmation message will be emailed to you. This message will have a
- link that you should click on to complete the removal process (you can
- also confirm by email; see the instructions in the confirmation
- message).""")])
-
- table.AddRow([Center(SubmitButton('login-unsub', _('Unsubscribe')))])
- # Password reminder section
- table.AddRow([Center(Header(2, _('Password reminder')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0,
- bgcolor=config.WEB_HEADER_COLOR)
-
- table.AddRow([_("""By clicking on the <em>Remind</em> button, your
- password will be emailed to you.""")])
-
- table.AddRow([Center(SubmitButton('login-remind', _('Remind')))])
- # Finish up glomming together the login page
- form.AddItem(table)
- doc.AddItem(form)
- doc.AddItem(mlist.GetMailmanFooter())
-
-
-
-def lists_of_member(mlist, user):
- hostname = mlist.host_name
- onlists = []
- for listname in config.list_manager.names:
- # The current list will always handle things in the mainline
- if listname == mlist.internal_name():
- continue
- glist = MailList.MailList(listname, lock=0)
- if glist.host_name <> hostname:
- continue
- if not glist.isMember(user):
- continue
- onlists.append(glist)
- return onlists
-
-
-
-def change_password(mlist, user, newpw):
- # Must own the list lock!
- mlist.Lock()
- try:
- # Change the user's password. The password must already have been
- # compared to the confirmpw and otherwise been vetted for
- # acceptability.
- mlist.setMemberPassword(user, newpw)
- mlist.Save()
- finally:
- mlist.Unlock()
-
-
-
-def global_options(mlist, user, globalopts):
- # Is there anything to do?
- for attr in dir(globalopts):
- if attr.startswith('_'):
- continue
- if getattr(globalopts, attr) is not None:
- break
- else:
- return
-
- def sigterm_handler(signum, frame, mlist=mlist):
- # Make sure the list gets unlocked...
- mlist.Unlock()
- # ...and ensure we exit, otherwise race conditions could cause us to
- # enter MailList.Save() while we're in the unlocked state, and that
- # could be bad!
- sys.exit(0)
-
- # Must own the list lock!
- mlist.Lock()
- try:
- if globalopts.enable is not None:
- mlist.setDeliveryStatus(user, globalopts.enable)
-
- if globalopts.remind is not None:
- mlist.setMemberOption(user, config.SuppressPasswordReminder,
- globalopts.remind)
-
- if globalopts.nodupes is not None:
- mlist.setMemberOption(user, config.DontReceiveDuplicates,
- globalopts.nodupes)
-
- if globalopts.mime is not None:
- mlist.setMemberOption(user, config.DisableMime, globalopts.mime)
-
- mlist.Save()
- finally:
- mlist.Unlock()
-
-
-
-def topic_details(mlist, doc, user, cpuser, userlang, varhelp):
- # Find out which topic the user wants to get details of
- reflist = varhelp.split('/')
- name = None
- topicname = _('<missing>')
- if len(reflist) == 1:
- topicname = urllib.unquote_plus(reflist[0])
- for name, pattern, description, emptyflag in mlist.topics:
- if name == topicname:
- break
- else:
- name = None
-
- if not name:
- options_page(mlist, doc, user, cpuser, userlang,
- _('Requested topic is not valid: %(topicname)s'))
- print doc.Format()
- return
-
- table = Table(border=3, width='100%')
- table.AddRow([Center(Bold(_('Topic filter details')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2,
- bgcolor=config.WEB_SUBHEADER_COLOR)
- table.AddRow([Bold(Label(_('Name:'))),
- Utils.websafe(name)])
- table.AddRow([Bold(Label(_('Pattern (as regexp):'))),
- '<pre>' + Utils.websafe(OR.join(pattern.splitlines()))
- + '</pre>'])
- table.AddRow([Bold(Label(_('Description:'))),
- Utils.websafe(description)])
- # Make colors look nice
- for row in range(1, 4):
- table.AddCellInfo(row, 0, bgcolor=config.WEB_ADMINITEM_COLOR)
-
- options_page(mlist, doc, user, cpuser, userlang, table.Format())
- print doc.Format()
diff --git a/mailman/web/Cgi/private.py b/mailman/web/Cgi/private.py
deleted file mode 100644
index 87cad7966..000000000
--- a/mailman/web/Cgi/private.py
+++ /dev/null
@@ -1,190 +0,0 @@
-# Copyright (C) 1998-2009 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""Provide a password-interface wrapper around private archives."""
-
-import os
-import sys
-import cgi
-import logging
-import mimetypes
-
-from Mailman import Errors
-from Mailman import MailList
-from Mailman import Utils
-from Mailman import i18n
-from Mailman.configuration import config
-from Mailman.htmlformat import *
-
-# Set up i18n. Until we know which list is being requested, we use the
-# server's default.
-_ = i18n._
-i18n.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
-SLASH = '/'
-
-log = logging.getLogger('mailman.error')
-mlog = logging.getLogger('mailman.mischief')
-
-
-
-def true_path(path):
- "Ensure that the path is safe by removing .."
- # Workaround for path traverse vulnerability. Unsuccessful attempts will
- # be logged in logs/error.
- parts = [x for x in path.split(SLASH) if x not in ('.', '..')]
- return SLASH.join(parts)[1:]
-
-
-
-def guess_type(url, strict):
- if hasattr(mimetypes, 'common_types'):
- return mimetypes.guess_type(url, strict)
- return mimetypes.guess_type(url)
-
-
-
-def main():
- doc = Document()
- doc.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
- parts = Utils.GetPathPieces()
- if not parts:
- doc.SetTitle(_("Private Archive Error"))
- doc.AddItem(Header(3, _("You must specify a list.")))
- print doc.Format()
- return
-
- path = os.environ.get('PATH_INFO')
- tpath = true_path(path)
- if tpath <> path[1:]:
- msg = _('Private archive - "./" and "../" not allowed in URL.')
- doc.SetTitle(msg)
- doc.AddItem(Header(2, msg))
- print doc.Format()
- mlog.error('Private archive hostile path: %s', path)
- return
- # BAW: This needs to be converted to the Site module abstraction
- true_filename = os.path.join(
- config.PRIVATE_ARCHIVE_FILE_DIR, tpath)
-
- listname = parts[0].lower()
- mboxfile = ''
- if len(parts) > 1:
- mboxfile = parts[1]
-
- # See if it's the list's mbox file is being requested
- if listname.endswith('.mbox') and mboxfile.endswith('.mbox') and \
- listname[:-5] == mboxfile[:-5]:
- listname = listname[:-5]
- else:
- mboxfile = ''
-
- # If it's a directory, we have to append index.html in this script. We
- # must also check for a gzipped file, because the text archives are
- # usually stored in compressed form.
- if os.path.isdir(true_filename):
- true_filename = true_filename + '/index.html'
- if not os.path.exists(true_filename) and \
- os.path.exists(true_filename + '.gz'):
- true_filename = true_filename + '.gz'
-
- try:
- mlist = MailList.MailList(listname, lock=0)
- except Errors.MMListError, e:
- # Avoid cross-site scripting attacks
- safelistname = Utils.websafe(listname)
- msg = _('No such list <em>%(safelistname)s</em>')
- doc.SetTitle(_("Private Archive Error - %(msg)s"))
- doc.AddItem(Header(2, msg))
- print doc.Format()
- log.error('No such list "%s": %s\n', listname, e)
- return
-
- i18n.set_language(mlist.preferred_language)
- doc.set_language(mlist.preferred_language)
-
- cgidata = cgi.FieldStorage()
- username = cgidata.getvalue('username', '')
- password = cgidata.getvalue('password', '')
-
- is_auth = 0
- realname = mlist.real_name
- message = ''
-
- if not mlist.WebAuthenticate((config.AuthUser,
- config.AuthListModerator,
- config.AuthListAdmin,
- config.AuthSiteAdmin),
- password, username):
- if cgidata.has_key('submit'):
- # This is a re-authorization attempt
- message = Bold(FontSize('+1', _('Authorization failed.'))).Format()
- # Output the password form
- charset = Utils.GetCharSet(mlist.preferred_language)
- print 'Content-type: text/html; charset=' + charset + '\n\n'
- # Put the original full path in the authorization form, but avoid
- # trailing slash if we're not adding parts. We add it below.
- action = mlist.GetScriptURL('private')
- if parts[1:]:
- action = os.path.join(action, SLASH.join(parts[1:]))
- # If we added '/index.html' to true_filename, add a slash to the URL.
- # We need this because we no longer add the trailing slash in the
- # private.html template. It's always OK to test parts[-1] since we've
- # already verified parts[0] is listname. The basic rule is if the
- # post URL (action) is a directory, it must be slash terminated, but
- # not if it's a file. Otherwise, relative links in the target archive
- # page don't work.
- if true_filename.endswith('/index.html') and parts[-1] <> 'index.html':
- action += SLASH
- # Escape web input parameter to avoid cross-site scripting.
- print Utils.maketext(
- 'private.html',
- {'action' : Utils.websafe(action),
- 'realname': mlist.real_name,
- 'message' : message,
- }, mlist=mlist)
- return
-
- lang = mlist.getMemberLanguage(username)
- i18n.set_language(lang)
- doc.set_language(lang)
-
- # Authorization confirmed... output the desired file
- try:
- ctype, enc = guess_type(path, strict=0)
- if ctype is None:
- ctype = 'text/html'
- if mboxfile:
- f = open(os.path.join(mlist.archive_dir() + '.mbox',
- mlist.internal_name() + '.mbox'))
- ctype = 'text/plain'
- elif true_filename.endswith('.gz'):
- import gzip
- f = gzip.open(true_filename, 'r')
- else:
- f = open(true_filename, 'r')
- except IOError:
- msg = _('Private archive file not found')
- doc.SetTitle(msg)
- doc.AddItem(Header(2, msg))
- print doc.Format()
- log.error('Private archive file not found: %s', true_filename)
- else:
- print 'Content-type: %s\n' % ctype
- sys.stdout.write(f.read())
- f.close()
diff --git a/mailman/web/Cgi/rmlist.py b/mailman/web/Cgi/rmlist.py
deleted file mode 100644
index 4b62f09fb..000000000
--- a/mailman/web/Cgi/rmlist.py
+++ /dev/null
@@ -1,243 +0,0 @@
-# Copyright (C) 2001-2009 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""Remove/delete mailing lists through the web."""
-
-import os
-import cgi
-import sys
-import errno
-import shutil
-import logging
-
-from Mailman import Errors
-from Mailman import MailList
-from Mailman import Utils
-from Mailman import i18n
-from Mailman.configuration import config
-from Mailman.htmlformat import *
-
-# Set up i18n
-_ = i18n._
-i18n.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
-log = logging.getLogger('mailman.error')
-mlog = logging.getLogger('mailman.mischief')
-
-
-
-def main():
- doc = Document()
- doc.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
- cgidata = cgi.FieldStorage()
- parts = Utils.GetPathPieces()
-
- if not parts:
- # Bad URL specification
- title = _('Bad URL specification')
- doc.SetTitle(title)
- doc.AddItem(
- Header(3, Bold(FontAttr(title, color='#ff0000', size='+2'))))
- doc.AddItem('<hr>')
- doc.AddItem(MailmanLogo())
- print doc.Format()
- log.error('Bad URL specification: %s', parts)
- return
-
- listname = parts[0].lower()
- try:
- mlist = MailList.MailList(listname, lock=0)
- except Errors.MMListError, e:
- # Avoid cross-site scripting attacks
- safelistname = Utils.websafe(listname)
- title = _('No such list <em>%(safelistname)s</em>')
- doc.SetTitle(title)
- doc.AddItem(
- Header(3,
- Bold(FontAttr(title, color='#ff0000', size='+2'))))
- doc.AddItem('<hr>')
- doc.AddItem(MailmanLogo())
- print doc.Format()
- log.error('No such list "%s": %s\n', listname, e)
- return
-
- # Now that we have a valid mailing list, set the language
- i18n.set_language(mlist.preferred_language)
- doc.set_language(mlist.preferred_language)
-
- # Be sure the list owners are not sneaking around!
- if not config.OWNERS_CAN_DELETE_THEIR_OWN_LISTS:
- title = _("You're being a sneaky list owner!")
- doc.SetTitle(title)
- doc.AddItem(
- Header(3, Bold(FontAttr(title, color='#ff0000', size='+2'))))
- doc.AddItem(mlist.GetMailmanFooter())
- print doc.Format()
- mlog.error('Attempt to sneakily delete a list: %s', listname)
- return
-
- if cgidata.has_key('doit'):
- process_request(doc, cgidata, mlist)
- print doc.Format()
- return
-
- request_deletion(doc, mlist)
- # Always add the footer and print the document
- doc.AddItem(mlist.GetMailmanFooter())
- print doc.Format()
-
-
-
-def process_request(doc, cgidata, mlist):
- password = cgidata.getvalue('password', '').strip()
- try:
- delarchives = int(cgidata.getvalue('delarchives', '0'))
- except ValueError:
- delarchives = 0
-
- # Removing a list is limited to the list-creator (a.k.a. list-destroyer),
- # the list-admin, or the site-admin. Don't use WebAuthenticate here
- # because we want to be sure the actual typed password is valid, not some
- # password sitting in a cookie.
- if mlist.Authenticate((config.AuthCreator,
- config.AuthListAdmin,
- config.AuthSiteAdmin),
- password) == config.UnAuthorized:
- request_deletion(
- doc, mlist,
- _('You are not authorized to delete this mailing list'))
- return
-
- # Do the MTA-specific list deletion tasks
- if config.MTA:
- modname = 'Mailman.MTA.' + config.MTA
- __import__(modname)
- sys.modules[modname].remove(mlist, cgi=1)
-
- REMOVABLES = ['lists/%s']
-
- if delarchives:
- REMOVABLES.extend(['archives/private/%s',
- 'archives/private/%s.mbox',
- 'archives/public/%s',
- 'archives/public/%s.mbox',
- ])
-
- problems = 0
- listname = mlist.internal_name()
- for dirtmpl in REMOVABLES:
- dir = os.path.join(config.VAR_DIR, dirtmpl % listname)
- if os.path.islink(dir):
- try:
- os.unlink(dir)
- except OSError, e:
- if e.errno not in (errno.EACCES, errno.EPERM): raise
- problems += 1
- log.error('link %s not deleted due to permission problems', dir)
- elif os.path.isdir(dir):
- try:
- shutil.rmtree(dir)
- except OSError, e:
- if e.errno not in (errno.EACCES, errno.EPERM): raise
- problems += 1
- log.error('directory %s not deleted due to permission problems',
- dir)
-
- title = _('Mailing list deletion results')
- doc.SetTitle(title)
- table = Table(border=0, width='100%')
- table.AddRow([Center(Bold(FontAttr(title, size='+1')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0,
- bgcolor=config.WEB_HEADER_COLOR)
- if not problems:
- table.AddRow([_('''You have successfully deleted the mailing list
- <b>%(listname)s</b>.''')])
- else:
- sitelist = mlist.no_reply_address
- table.AddRow([_('''There were some problems deleting the mailing list
- <b>%(listname)s</b>. Contact your site administrator at %(sitelist)s
- for details.''')])
- doc.AddItem(table)
- doc.AddItem('<hr>')
- doc.AddItem(_('Return to the ') +
- Link(Utils.ScriptURL('listinfo'),
- _('general list overview')).Format())
- doc.AddItem(_('<br>Return to the ') +
- Link(Utils.ScriptURL('admin'),
- _('administrative list overview')).Format())
- doc.AddItem(MailmanLogo())
-
-
-
-def request_deletion(doc, mlist, errmsg=None):
- realname = mlist.real_name
- title = _('Permanently remove mailing list <em>%(realname)s</em>')
- doc.SetTitle(title)
-
- table = Table(border=0, width='100%')
- table.AddRow([Center(Bold(FontAttr(title, size='+1')))])
- table.AddCellInfo(table.GetCurrentRowIndex(), 0,
- bgcolor=config.WEB_HEADER_COLOR)
-
- # Add any error message
- if errmsg:
- table.AddRow([Header(3, Bold(
- FontAttr(_('Error: '), color='#ff0000', size='+2').Format() +
- Italic(errmsg).Format()))])
-
- table.AddRow([_("""This page allows you as the list owner, to permanent
- remove this mailing list from the system. <strong>This action is not
- undoable</strong> so you should undertake it only if you are absolutely
- sure this mailing list has served its purpose and is no longer necessary.
-
- <p>Note that no warning will be sent to your list members and after this
- action, any subsequent messages sent to the mailing list, or any of its
- administrative addreses will bounce.
-
- <p>You also have the option of removing the archives for this mailing list
- at this time. It is almost always recommended that you do
- <strong>not</strong> remove the archives, since they serve as the
- historical record of your mailing list.
-
- <p>For your safety, you will be asked to reconfirm the list password.
- """)])
- GREY = config.WEB_ADMINITEM_COLOR
- form = Form(mlist.GetScriptURL('rmlist'))
- ftable = Table(border=0, cols='2', width='100%',
- cellspacing=3, cellpadding=4)
-
- ftable.AddRow([Label(_('List password:')), PasswordBox('password')])
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
-
- ftable.AddRow([Label(_('Also delete archives?')),
- RadioButtonArray('delarchives', (_('No'), _('Yes')),
- checked=0, values=(0, 1))])
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, bgcolor=GREY)
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 1, bgcolor=GREY)
-
- ftable.AddRow([Center(Link(
- mlist.GetScriptURL('admin'),
- _('<b>Cancel</b> and return to list administration')))])
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, colspan=2)
-
- ftable.AddRow([Center(SubmitButton('doit', _('Delete this list')))])
- ftable.AddCellInfo(ftable.GetCurrentRowIndex(), 0, colspan=2)
- form.AddItem(ftable)
- table.AddRow([form])
- doc.AddItem(table)
diff --git a/mailman/web/Cgi/roster.py b/mailman/web/Cgi/roster.py
deleted file mode 100644
index 2351d6915..000000000
--- a/mailman/web/Cgi/roster.py
+++ /dev/null
@@ -1,130 +0,0 @@
-# Copyright (C) 1998-2009 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""Produce subscriber roster, using listinfo form data, roster.html template.
-
-Takes listname in PATH_INFO.
-"""
-
-
-# We don't need to lock in this script, because we're never going to change
-# data.
-
-import os
-import cgi
-import sys
-import urllib
-import logging
-
-from Mailman import Errors
-from Mailman import MailList
-from Mailman import Utils
-from Mailman import i18n
-from Mailman.configuration import config
-from Mailman.htmlformat import *
-
-# Set up i18n
-_ = i18n._
-i18n.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
-log = logging.getLogger('mailman.error')
-
-
-
-def main():
- parts = Utils.GetPathPieces()
- if not parts:
- error_page(_('Invalid options to CGI script'))
- return
-
- listname = parts[0].lower()
- try:
- mlist = MailList.MailList(listname, lock=0)
- except Errors.MMListError, e:
- # Avoid cross-site scripting attacks
- safelistname = Utils.websafe(listname)
- error_page(_('No such list <em>%(safelistname)s</em>'))
- log.error('roster: no such list "%s": %s', listname, e)
- return
-
- cgidata = cgi.FieldStorage()
-
- # messages in form should go in selected language (if any...)
- lang = cgidata.getvalue('language')
- if lang not in config.languages.enabled_codes:
- lang = mlist.preferred_language
- i18n.set_language(lang)
-
- # Perform authentication for protected rosters. If the roster isn't
- # protected, then anybody can see the pages. If members-only or
- # "admin"-only, then we try to cookie authenticate the user, and failing
- # that, we check roster-email and roster-pw fields for a valid password.
- # (also allowed: the list moderator, the list admin, and the site admin).
- if mlist.private_roster == 0:
- # No privacy
- ok = 1
- elif mlist.private_roster == 1:
- # Members only
- addr = cgidata.getvalue('roster-email', '')
- password = cgidata.getvalue('roster-pw', '')
- ok = mlist.WebAuthenticate((config.AuthUser,
- config.AuthListModerator,
- config.AuthListAdmin,
- config.AuthSiteAdmin),
- password, addr)
- else:
- # Admin only, so we can ignore the address field
- password = cgidata.getvalue('roster-pw', '')
- ok = mlist.WebAuthenticate((config.AuthListModerator,
- config.AuthListAdmin,
- config.AuthSiteAdmin),
- password)
- if not ok:
- realname = mlist.real_name
- doc = Document()
- doc.set_language(lang)
- error_page_doc(doc, _('%(realname)s roster authentication failed.'))
- doc.AddItem(mlist.GetMailmanFooter())
- print doc.Format()
- return
-
- # The document and its language
- doc = HeadlessDocument()
- doc.set_language(lang)
-
- replacements = mlist.GetAllReplacements(lang)
- replacements['<mm-displang-box>'] = mlist.FormatButton(
- 'displang-button',
- text = _('View this page in'))
- replacements['<mm-lang-form-start>'] = mlist.FormatFormStart('roster')
- doc.AddItem(mlist.ParseTags('roster.html', replacements, lang))
- print doc.Format()
-
-
-
-def error_page(errmsg):
- doc = Document()
- doc.set_language(config.DEFAULT_SERVER_LANGUAGE)
- error_page_doc(doc, errmsg)
- print doc.Format()
-
-
-def error_page_doc(doc, errmsg, *args):
- # Produce a simple error-message page on stdout and exit.
- doc.SetTitle(_("Error"))
- doc.AddItem(Header(2, _("Error")))
- doc.AddItem(Bold(errmsg % args))
diff --git a/mailman/web/Cgi/subscribe.py b/mailman/web/Cgi/subscribe.py
deleted file mode 100644
index 0eefd0111..000000000
--- a/mailman/web/Cgi/subscribe.py
+++ /dev/null
@@ -1,252 +0,0 @@
-# Copyright (C) 1998-2009 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""Process subscription or roster requests from listinfo form."""
-
-from __future__ import with_statement
-
-import os
-import cgi
-import sys
-import logging
-
-from Mailman import Errors
-from Mailman import MailList
-from Mailman import Message
-from Mailman import Utils
-from Mailman import i18n
-from Mailman.UserDesc import UserDesc
-from Mailman.configuration import config
-from Mailman.htmlformat import *
-
-SLASH = '/'
-ERRORSEP = '\n\n<p>'
-
-# Set up i18n
-_ = i18n._
-i18n.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
-log = logging.getLogger('mailman.error')
-mlog = logging.getLogger('mailman.mischief')
-
-
-
-def main():
- doc = Document()
- doc.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
- parts = Utils.GetPathPieces()
- if not parts:
- doc.AddItem(Header(2, _("Error")))
- doc.AddItem(Bold(_('Invalid options to CGI script')))
- print doc.Format()
- return
-
- listname = parts[0].lower()
- try:
- mlist = MailList.MailList(listname, lock=0)
- except Errors.MMListError, e:
- # Avoid cross-site scripting attacks
- safelistname = Utils.websafe(listname)
- doc.AddItem(Header(2, _("Error")))
- doc.AddItem(Bold(_('No such list <em>%(safelistname)s</em>')))
- print doc.Format()
- log.error('No such list "%s": %s\n', listname, e)
- return
-
- # See if the form data has a preferred language set, in which case, use it
- # for the results. If not, use the list's preferred language.
- cgidata = cgi.FieldStorage()
- language = cgidata.getvalue('language')
- if language not in config.languages.enabled_codes:
- language = mlist.preferred_language
- i18n.set_language(language)
- doc.set_language(language)
-
- mlist.Lock()
- try:
- process_form(mlist, doc, cgidata, language)
- mlist.Save()
- finally:
- mlist.Unlock()
-
-
-
-def process_form(mlist, doc, cgidata, lang):
- listowner = mlist.GetOwnerEmail()
- realname = mlist.real_name
- results = []
-
- # The email address being subscribed, required
- email = cgidata.getvalue('email', '')
- if not email:
- results.append(_('You must supply a valid email address.'))
-
- fullname = cgidata.getvalue('fullname', '')
- # Canonicalize the full name
- fullname = Utils.canonstr(fullname, lang)
- # Who was doing the subscribing?
- remote = os.environ.get('REMOTE_HOST',
- os.environ.get('REMOTE_ADDR',
- 'unidentified origin'))
- # Was an attempt made to subscribe the list to itself?
- if email == mlist.GetListEmail():
- mlog.error('Attempt to self subscribe %s: %s', email, remote)
- results.append(_('You may not subscribe a list to itself!'))
- # If the user did not supply a password, generate one for him
- password = cgidata.getvalue('pw')
- confirmed = cgidata.getvalue('pw-conf')
-
- if password is None and confirmed is None:
- password = Utils.MakeRandomPassword()
- elif password is None or confirmed is None:
- results.append(_('If you supply a password, you must confirm it.'))
- elif password <> confirmed:
- results.append(_('Your passwords did not match.'))
-
- # Get the digest option for the subscription.
- digestflag = cgidata.getvalue('digest')
- if digestflag:
- try:
- digest = int(digestflag)
- except ValueError:
- digest = 0
- else:
- digest = mlist.digest_is_default
-
- # Sanity check based on list configuration. BAW: It's actually bogus that
- # the page allows you to set the digest flag if you don't really get the
- # choice. :/
- if not mlist.digestable:
- digest = 0
- elif not mlist.nondigestable:
- digest = 1
-
- if results:
- print_results(mlist, ERRORSEP.join(results), doc, lang)
- return
-
- # If this list has private rosters, we have to be careful about the
- # message that gets printed, otherwise the subscription process can be
- # used to mine for list members. It may be inefficient, but it's still
- # possible, and that kind of defeats the purpose of private rosters.
- # We'll use this string for all successful or unsuccessful subscription
- # results.
- if mlist.private_roster == 0:
- # Public rosters
- privacy_results = ''
- else:
- privacy_results = _("""\
-Your subscription request has been received, and will soon be acted upon.
-Depending on the configuration of this mailing list, your subscription request
-may have to be first confirmed by you via email, or approved by the list
-moderator. If confirmation is required, you will soon get a confirmation
-email which contains further instructions.""")
-
- try:
- userdesc = UserDesc(email, fullname, password, digest, lang)
- mlist.AddMember(userdesc, remote)
- results = ''
- # Check for all the errors that mlist.AddMember can throw options on the
- # web page for this cgi
- except Errors.MembershipIsBanned:
- results = _("""The email address you supplied is banned from this
- mailing list. If you think this restriction is erroneous, please
- contact the list owners at %(listowner)s.""")
- except Errors.InvalidEmailAddress:
- results = _('The email address you supplied is not valid.')
- except Errors.MMSubscribeNeedsConfirmation:
- # Results string depends on whether we have private rosters or not
- if privacy_results:
- results = privacy_results
- else:
- results = _("""\
-Confirmation from your email address is required, to prevent anyone from
-subscribing you without permission. Instructions are being sent to you at
-%(email)s. Please note your subscription will not start until you confirm
-your subscription.""")
- except Errors.MMNeedApproval, x:
- # Results string depends on whether we have private rosters or not
- if privacy_results:
- results = privacy_results
- else:
- # We need to interpolate into x
- x = _(x)
- results = _("""\
-Your subscription request was deferred because %(x)s. Your request has been
-forwarded to the list moderator. You will receive email informing you of the
-moderator's decision when they get to your request.""")
- except Errors.MMAlreadyAMember:
- # Results string depends on whether we have private rosters or not
- if not privacy_results:
- results = _('You are already subscribed.')
- else:
- results = privacy_results
- # This could be a membership probe. For safety, let the user know
- # a probe occurred. BAW: should we inform the list moderator?
- listaddr = mlist.GetListEmail()
- # Set the language for this email message to the member's language.
- mlang = mlist.getMemberLanguage(email)
- with i18n.using_language(mlang):
- msg = Message.UserNotification(
- mlist.getMemberCPAddress(email),
- mlist.GetBouncesEmail(),
- _('Mailman privacy alert'),
- _("""\
-An attempt was made to subscribe your address to the mailing list
-%(listaddr)s. You are already subscribed to this mailing list.
-
-Note that the list membership is not public, so it is possible that a bad
-person was trying to probe the list for its membership. This would be a
-privacy violation if we let them do this, but we didn't.
-
-If you submitted the subscription request and forgot that you were already
-subscribed to the list, then you can ignore this message. If you suspect that
-an attempt is being made to covertly discover whether you are a member of this
-list, and you are worried about your privacy, then feel free to send a message
-to the list administrator at %(listowner)s.
-"""), lang=mlang)
- msg.send(mlist)
- # These shouldn't happen unless someone's tampering with the form
- except Errors.MMCantDigestError:
- results = _('This list does not support digest delivery.')
- except Errors.MMMustDigestError:
- results = _('This list only supports digest delivery.')
- else:
- # Everything's cool. Our return string actually depends on whether
- # this list has private rosters or not
- if privacy_results:
- results = privacy_results
- else:
- results = _("""\
-You have been successfully subscribed to the %(realname)s mailing list.""")
- # Show the results
- print_results(mlist, results, doc, lang)
-
-
-
-def print_results(mlist, results, doc, lang):
- # The bulk of the document will come from the options.html template, which
- # includes its own html armor (head tags, etc.). Suppress the head that
- # Document() derived pages get automatically.
- doc.suppress_head = 1
-
- replacements = mlist.GetStandardReplacements(lang)
- replacements['<mm-results>'] = results
- output = mlist.ParseTags('subscribe.html', replacements, lang)
- doc.AddItem(output)
- print doc.Format()
diff --git a/mailman/web/Cgi/wsgi_app.py b/mailman/web/Cgi/wsgi_app.py
deleted file mode 100644
index b22dbf452..000000000
--- a/mailman/web/Cgi/wsgi_app.py
+++ /dev/null
@@ -1,286 +0,0 @@
-# Copyright (C) 2006-2009 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-import os
-import re
-import sys
-
-from cStringIO import StringIO
-from email import message_from_string
-from urlparse import urlparse
-
-from Mailman.configuration import config
-
-# XXX Should this be configurable in Defaults.py?
-STEALTH_MODE = False
-MOVED_RESPONSE = '302 Found'
-# Above is for debugging convenience. We should use:
-# MOVED_RESPONSE = '301 Moved Permanently'
-
-
-
-def websafe(s):
- return s
-
-
-SCRIPTS = ['admin', 'admindb', 'confirm', 'create',
- 'edithtml', 'listinfo', 'options', 'private',
- 'rmlist', 'roster', 'subscribe']
-ARCHVIEW = ['private']
-
-SLASH = '/'
-NL2 = '\n\n'
-CRLF2 = '\r\n\r\n'
-
-dotonly = re.compile(r'^\.+$')
-
-
-
-# WSGI to CGI wrapper. Mostly copied from scripts/driver.
-def mailman_app(environ, start_response):
- """Wrapper to *.py CGI commands"""
- global STEALTH_MODE, websafe
- try:
- try:
- if not STEALTH_MODE:
- from Mailman.Utils import websafe
- except:
- STEALTH_MODE = True
- raise
-
- import logging
- log = logging.getLogger('mailman.error')
-
- from Mailman import i18n
- i18n.set_language(config.DEFAULT_SERVER_LANGUAGE)
-
- path = environ['PATH_INFO']
- paths = path.split(SLASH)
- # sanity check for paths
- spaths = [ i for i in paths[1:] if i and not dotonly.match(i) ]
- if spaths and spaths != paths[1:]:
- newpath = SLASH + SLASH.join(spaths)
- start_response(MOVED_RESPONSE, [('Location', newpath)])
- return 'Location: ' + newpath
- # find script name
- for script in SCRIPTS:
- if script in spaths:
- # Get script position in spaths and break.
- scrpos = spaths.index(script)
- break
- else:
- # Can't find valid script.
- start_response('404 Not Found', [])
- return '404 Not Found'
- # Compose CGI SCRIPT_NAME and PATH_INFO from WSGI path.
- script_name = SLASH + SLASH.join(spaths[:scrpos+1])
- environ['SCRIPT_NAME'] = script_name
- if len(paths) > scrpos+2:
- path_info = SLASH + SLASH.join(paths[scrpos+2:])
- if script in ARCHVIEW \
- and path_info.count('/') in (1,2) \
- and not paths[-1].split('.')[-1] in ('html', 'txt', 'gz'):
- # Add index.html if /private/listname or
- # /private/listname/YYYYmm is requested.
- newpath = script_name + path_info + '/index.html'
- start_response(MOVED_RESPONSE, [('Location', newpath)])
- return 'Location: ' + newpath
- environ['PATH_INFO'] = path_info
- else:
- environ['PATH_INFO'] = ''
- # Reverse proxy environment.
- if environ.has_key('HTTP_X_FORWARDED_HOST'):
- environ['HTTP_HOST'] = environ['HTTP_X_FORWARDED_HOST']
- if environ.has_key('HTTP_X_FORWARDED_FOR'):
- environ['REMOTE_HOST'] = environ['HTTP_X_FORWARDED_FOR']
- modname = 'Mailman.Cgi.' + script
- # Clear previous cookie before setting new one.
- os.environ['HTTP_COOKIE'] = ''
- for k, v in environ.items():
- os.environ[k] = str(v)
- # Prepare for redirection
- save_stdin = sys.stdin
- # CGI writes its output to sys.stdout, while wsgi app should
- # return (list of) strings.
- save_stdout = sys.stdout
- save_stderr = sys.stderr
- tmpstdout = StringIO()
- tmpstderr = StringIO()
- response = ''
- try:
- try:
- sys.stdin = environ['wsgi.input']
- sys.stdout = tmpstdout
- sys.stderr = tmpstderr
- __import__(modname)
- sys.modules[modname].main()
- response = sys.stdout.getvalue()
- finally:
- sys.stdin = save_stdin
- sys.stdout = save_stdout
- sys.stderr = save_stderr
- except SystemExit:
- sys.stdout.write(tmpstdout.getvalue())
- if response:
- try:
- head, content = response.split(NL2, 1)
- except ValueError:
- head, content = response.split(CRLF2, 1)
- m = message_from_string(head + CRLF2)
- start_response('200 OK', m.items())
- return [content]
- else:
- # TBD: Error Code/Message
- start_response('500 Server Error', [])
- return '500 Internal Server Error'
- except:
- start_response('200 OK', [('Content-Type', 'text/html')])
- retstring = print_traceback(log)
- retstring += print_environment(log)
- return retstring
-
-
-
-# These functions are extracted and modified from scripts/driver.
-#
-# If possible, we print the error to two places. One will always be stdout
-# and the other will be the log file if a log file was created. It is assumed
-# that stdout is an HTML sink.
-def print_traceback(log=None):
- try:
- import traceback
- except ImportError:
- traceback = None
- try:
- from mailman.version import VERSION
- except ImportError:
- VERSION = '&lt;undetermined&gt;'
-
- # Write to the log file first.
- if log:
- outfp = StringIO()
-
- print >> outfp, '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'
- print >> outfp, '[----- Mailman Version: %s -----]' % VERSION
- print >> outfp, '[----- Traceback ------]'
- if traceback:
- traceback.print_exc(file=outfp)
- else:
- print >> outfp, '[failed to import module traceback]'
- print >> outfp, '[exc: %s, var: %s]' % sys.exc_info()[0:2]
- # Don't use .exception() since that'll give us the exception twice.
- # IWBNI we could print directly to the log's stream, or treat a log
- # like an output stream.
- log.error('%s', outfp.getvalue())
-
- # return HTML sink.
- htfp = StringIO()
- print >> htfp, """\
-<head><title>Bug in Mailman version %(VERSION)s</title></head>
-<body bgcolor=#ffffff><h2>Bug in Mailman version %(VERSION)s</h2>
-<p><h3>We're sorry, we hit a bug!</h3>
-""" % locals()
- if not STEALTH_MODE:
- print >> htfp, '''<p>If you would like to help us identify the problem,
-please email a copy of this page to the webmaster for this site with
-a description of what happened. Thanks!
-
-<h4>Traceback:</h4><p><pre>'''
- exc_info = sys.exc_info()
- if traceback:
- for line in traceback.format_exception(*exc_info):
- print >> htfp, websafe(line)
- else:
- print >> htfp, '[failed to import module traceback]'
- print >> htfp, '[exc: %s, var: %s]' %\
- [websafe(x) for x in exc_info[0:2]]
- print >> htfp, '\n\n</pre></body>'
- else:
- print >> htfp, '''<p>Please inform the webmaster for this site of this
-problem. Printing of traceback and other system information has been
-explicitly inhibited, but the webmaster can find this information in the
-Mailman error logs.'''
- return htfp.getvalue()
-
-
-
-def print_environment(log=None):
- try:
- import os
- except ImportError:
- os = None
-
- if log:
- outfp = StringIO()
-
- # Write some information about our Python executable to the log file.
- print >> outfp, '[----- Python Information -----]'
- print >> outfp, 'sys.version =', sys.version
- print >> outfp, 'sys.executable =', sys.executable
- print >> outfp, 'sys.prefix =', sys.prefix
- print >> outfp, 'sys.exec_prefix =', sys.exec_prefix
- print >> outfp, 'sys.path =', sys.exec_prefix
- print >> outfp, 'sys.platform =', sys.platform
-
- # Write the same information to the HTML sink.
- htfp = StringIO()
- if not STEALTH_MODE:
- print >> htfp, """\
-<p><hr><h4>Python information:</h4>
-
-<p><table>
-<tr><th>Variable</th><th>Value</th></tr>
-<tr><td><tt>sys.version</tt></td><td> %s </td></tr>
-<tr><td><tt>sys.executable</tt></td><td> %s </td></tr>
-<tr><td><tt>sys.prefix</tt></td><td> %s </td></tr>
-<tr><td><tt>sys.exec_prefix</tt></td><td> %s </td></tr>
-<tr><td><tt>sys.path</tt></td><td> %s </td></tr>
-<tr><td><tt>sys.platform</tt></td><td> %s </td></tr>
-</table>""" % (sys.version, sys.executable, sys.prefix,
- sys.exec_prefix, sys.path, sys.platform)
-
- # Write environment variables to the log file.
- if log:
- print >> outfp, '[----- Environment Variables -----]'
- if os:
- for k, v in os.environ.items():
- print >> outfp, '\t%s: %s' % (k, v)
- else:
- print >> outfp, '[failed to import module os]'
-
- # Write environment variables to the HTML sink.
- if not STEALTH_MODE:
- print >> htfp, """\
-<p><hr><h4>Environment variables:</h4>
-
-<p><table>
-<tr><th>Variable</th><th>Value</th></tr>
-"""
- if os:
- for k, v in os.environ.items():
- print >> htfp, '<tr><td><tt>' + websafe(k) + \
- '</tt></td><td>' + websafe(v) + \
- '</td></tr>'
- print >> htfp, '</table>'
- else:
- print >> htfp, '<p><hr>[failed to import module os]'
-
- # Dump the log output
- if log:
- log.error('%s', outfp.getvalue())
-
- return htfp.getvalue()