summaryrefslogtreecommitdiff
path: root/Mailman/Cgi/admin.py
diff options
context:
space:
mode:
Diffstat (limited to 'Mailman/Cgi/admin.py')
-rw-r--r--Mailman/Cgi/admin.py835
1 files changed, 835 insertions, 0 deletions
diff --git a/Mailman/Cgi/admin.py b/Mailman/Cgi/admin.py
new file mode 100644
index 000000000..c9155c87e
--- /dev/null
+++ b/Mailman/Cgi/admin.py
@@ -0,0 +1,835 @@
+#! /usr/bin/env python
+#
+# Copyright (C) 1998 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+"""Process and produce the list-administration options forms.
+
+To run stand-alone for debugging, set env var PATH_INFO to name of list
+and, optionally, options category."""
+
+__version__ = "$Revision: 734 $"
+
+import sys
+import os, cgi, string, crypt, types, time
+import paths # path hacking
+import mm_utils, maillist, mm_cfg, mm_err, mm_mailcmd, Cookie
+from htmlformat import *
+
+try:
+ sys.stderr = mm_utils.StampedLogger("error", label = 'admin',
+ manual_reprime=1, nofail=0)
+except IOError:
+ pass # Oh well - SOL on redirect, errors show thru.
+
+CATEGORIES = [('general', "General Options"),
+ ('members', "Membership Management"),
+ ('privacy', "Privacy Options"),
+ ('nondigest', "Regular-member (non-digest) Options"),
+ ('digest', "Digest-member Options"),
+ ('bounce', "Bounce Options"),
+ ('archive', "Archival Options"),
+ ('gateway', "Mail-News and News-Mail gateways")]
+
+
+LOGIN_PAGE = """
+<html>
+<head>
+ <title>%(listname)s Administrative Authentication</title>
+</head>
+<body bgcolor="#ffffff">
+<FORM METHOD=POST ACTION="%(path)s">
+%(message)s
+ <TABLE WIDTH="100%%" BORDER="0" CELLSPACING="4" CELLPADDING="5">
+ <TR>
+ <TD COLSPAN="2" WIDTH="100%%" BGCOLOR="#99CCFF" ALIGN="CENTER">
+ <B><FONT COLOR="#000000" SIZE="+1">%(listname)s Administrative
+ Authentication</FONT></B>
+ </TD>
+ </TR>
+ <tr>
+ <TD> <div ALIGN="Right"> List Administrative Password: </div> </TD>
+ <TD> <INPUT TYPE=password NAME=adminpw SIZE=30></TD>
+ </tr>
+ <tr>
+ <td colspan=2 align=middle> <INPUT TYPE=SUBMIT name="request_login">
+ </td>
+ </tr>
+ </TABLE>
+</FORM>
+"""
+
+# " <- icky emacs font-lock bug workaround
+
+SECRET="monty"
+
+def isAuthenticated(list, password=None, SECRET="SECRET"):
+ import base64, md5
+ if password is not None: # explicit login
+ try:
+ list.ConfirmAdminPassword(password)
+ except mm_err.MMBadPasswordError:
+ AddErrorMessage(doc, 'Error: Incorrect admin password.')
+ return 0
+ except:
+ print "Content-type: text/html\n"
+
+ print "<p><h3>We're sorry, we hit a bug!</h3>\n"
+ print "If you would like to help us identify the problem, please "
+ print "email a copy of this page to the webmaster for this site"
+ print 'with a description of what happened. Thanks!'
+ print "\n<PRE>"
+ try:
+ import traceback
+ sys.stderr = sys.stdout
+ traceback.print_exc()
+ except:
+ print "[failed to get traceback]"
+ print "\n\n</PRE>"
+
+ token = md5.new(SECRET + list_name + SECRET).digest()
+ token = base64.encodestring(token)
+ token = string.strip(token)
+ c = Cookie.Cookie()
+ cookie_key = list_name + "-admin"
+ c[cookie_key] = token
+ c[cookie_key]['expires'] = mm_cfg.ADMIN_COOKIE_LIFE
+ print c # Output the cookie
+ return 1
+ if os.environ.has_key('HTTP_COOKIE'):
+ c = Cookie.Cookie( os.environ['HTTP_COOKIE'] )
+ if c.has_key(list_name + "-admin"):
+ try:
+ inp = base64.decodestring(c[list_name + "-admin"].value)
+ check = md5.new(SECRET+list_name+SECRET).digest()
+ except Error: # the decodestring may return incorrect padding?
+ raise 'Decode failed'
+ return 0
+ if inp == check:
+ return 1
+ else:
+ return 0
+ return 0
+
+
+def main():
+ """Process and produce list options form.
+
+ CGI input indicates that we're returning from submission of some new
+ settings, which is processed before producing the new version."""
+ global list_name, list_info, doc
+ doc = Document()
+
+ try:
+ path = os.environ['PATH_INFO']
+ except KeyError:
+ path = ""
+ list_info = mm_utils.GetPathPieces(path)
+ # How many ../'s we need to get back to http://host/mailman
+
+ if len(list_info) == 0:
+ FormatAdminOverview()
+ return
+
+ list_name = string.lower(list_info[0])
+
+ try:
+ lst = maillist.MailList(list_name)
+ except mm_err.MMUnknownListError:
+ lst = None
+ try:
+ if not (lst and lst._ready):
+ FormatAdminOverview(error="List <em>%s</em> not found."
+ % list_name)
+ return
+
+ if len(list_info) == 1:
+ category = 'general'
+ category_suffix = ''
+ else:
+ category = list_info[1]
+ category_suffix = category
+
+ if category not in map(lambda x: x[0], CATEGORIES):
+ category = 'general'
+ global cgi_data
+ cgi_data = cgi.FieldStorage()
+ is_auth = 0
+ if cgi_data.has_key("adminpw"):
+ is_auth = isAuthenticated(lst, cgi_data["adminpw"].value)
+ message = FontAttr("Sorry, wrong password. Try again.",
+ color="ff5060", size="+1").Format()
+ else:
+ is_auth = isAuthenticated(lst)
+ message = ""
+ if not is_auth:
+ print "Content-type: text/html\n\n"
+ print LOGIN_PAGE % ({"listname": list_name,
+ "path": os.environ.get("REQUEST_URI", "/mailman/admin"),
+ "message": message})
+ return
+
+ if len(cgi_data.keys()):
+ if cgi_data.has_key('VARHELP'):
+ FormatOptionHelp(doc, cgi_data['VARHELP'].value, lst)
+ print doc.Format(bgcolor="#ffffff")
+ return
+ if (cgi_data.has_key('bounce_matching_headers')):
+ try:
+ pairs = lst.parse_matching_header_opt()
+ except mm_err.MMBadConfigError, line:
+ AddErrorMessage(doc,
+ 'Warning: bad matching-header line'
+ ' (does it have the colon?)<ul> %s </ul>',
+ line)
+
+ if not lst.digestable and len(lst.digest_members):
+ AddErrorMessage(doc,
+ 'Warning: you have digest members,'
+ ' but digests are turned off.'
+ ' Those people will not receive mail.')
+ if not lst.nondigestable and len(lst.members):
+ AddErrorMessage(doc,
+ 'Warning: you have lst members,'
+ ' but non-digestified mail is turned'
+ ' off. They will receive mail until'
+ ' you fix this problem.')
+ if len(cgi_data.keys()):
+ ChangeOptions(lst, category, cgi_data, doc)
+ FormatConfiguration(doc, lst, category, category_suffix)
+ print doc.Format(bgcolor="#ffffff")
+
+ finally:
+ if lst:
+ lst.Unlock()
+
+# Form Production:
+
+def FormatAdminOverview(error=None):
+ "Present a general welcome and itemize the (public) lists."
+ doc = Document()
+ legend = "%s maillists - Admin Links" % mm_cfg.DEFAULT_HOST_NAME
+ doc.SetTitle(legend)
+
+ table = Table(border=0, width="100%")
+ table.AddRow([Center(Header(2, legend))])
+ table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 0,
+ colspan=2, bgcolor="#99ccff")
+
+ advertised = []
+ names = mm_utils.list_names()
+ names.sort()
+ for n in names:
+ l = maillist.MailList(n, lock=0)
+ if l.advertised: advertised.append(l)
+
+ if error:
+ greeting = FontAttr(error, color="ff5060", size="+1")
+ else:
+ greeting = "Welcome!"
+
+ if not advertised:
+ welcome_items = (greeting,
+ "<p>"
+ " There currently are no publicly-advertised ",
+ Link(mm_cfg.MAILMAN_URL, "mailman"),
+ " maillists on %s." % mm_cfg.DEFAULT_HOST_NAME,
+ )
+ else:
+
+ welcome_items = (
+ greeting,
+ "<p>"
+ " Below is the collection of publicly-advertised ",
+ Link(mm_cfg.MAILMAN_URL, "mailman"),
+ " maillists on %s." % mm_cfg.DEFAULT_HOST_NAME,
+ (' Click on a list name to visit the configuration pages'
+ ' for that list.'
+ )
+ )
+
+ welcome_items = (welcome_items +
+ (" To visit the administrators configuration page for"
+ " an unadvertised list, open a URL similar to this"
+ +
+ (" one, but with a '/' and the %slist name appended.<p>"
+ % ((error and "the right ") or ""))
+ +
+ " General list information can be found at ",
+ Link(os.path.join('../'* mm_utils.GetNestingLevel(),
+ "listinfo/"), "the maillist overview page"),
+ "."
+ "<p>(Send questions and comments to ",
+ Link("mailto:%s" % mm_cfg.MAILMAN_OWNER,
+ mm_cfg.MAILMAN_OWNER),
+ ".)<p>"
+ )
+ )
+
+ table.AddRow([apply(Container, welcome_items)])
+ table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 0, colspan=2)
+
+ if advertised:
+ table.AddRow([Italic("List"), Italic("Description")])
+ for l in advertised:
+ table.AddRow([Link(l.GetRelativeScriptURL('admin'),
+ Bold(l.real_name)),l.description])
+
+ doc.AddItem(table)
+
+ print doc.Format(bgcolor="#ffffff")
+
+def FormatConfiguration(doc, lst, category, category_suffix):
+ """Produce the overall doc, *except* any processing error messages."""
+ for k, v in CATEGORIES:
+ if k == category: label = v
+
+ doc.SetTitle('%s Administration' % lst.real_name)
+ doc.AddItem(Center(Header(2, ('%s Maillist Configuration - %s Section'
+ % (lst.real_name, label)))))
+ doc.AddItem('<hr>')
+
+ links_table = Table(valign="top")
+
+ links_table.AddRow([Center(Bold("Configuration Categories")),
+ Center(Bold("Other Administrative Activities"))])
+ other_links = UnorderedList()
+ link = Link(lst.GetRelativeScriptURL('admindb'),
+ 'Tend to pending administrative requests.')
+ other_links.AddItem(link)
+ link = Link(lst.GetRelativeScriptURL('listinfo'),
+ 'Go to the general list information page.')
+ other_links.AddItem(link)
+ link = Link(lst.GetRelativeScriptURL('edithtml'),
+ 'Edit the HTML for the public list pages.')
+ other_links.AddItem(link)
+
+ these_links = UnorderedList()
+ for k, v in CATEGORIES:
+ if k == category:
+ these_links.AddItem("<b> =&gt; " + v + " &lt;= </b>")
+ else:
+ these_links.AddItem(Link("%s/%s" %
+ (lst.GetRelativeScriptURL('admin'),k),v))
+
+ links_table.AddRow([these_links, other_links])
+ links_table.AddRowInfo(max(links_table.GetCurrentRowIndex(), 0),
+ valign="top")
+
+ doc.AddItem(links_table)
+ doc.AddItem('<hr>')
+ if category_suffix:
+ form = Form("%s/%s" % (lst.GetRelativeScriptURL('admin'),
+ category_suffix))
+ else:
+ form = Form(lst.GetRelativeScriptURL('admin'))
+ doc.AddItem(form)
+
+ form.AddItem("Make your changes, below, and then submit it all at the"
+ " bottom. (You can also change your password there,"
+ " as well.)<p>")
+
+ form.AddItem(FormatOptionsSection(category, lst))
+
+ form.AddItem(Center(FormatPasswordStuff()))
+
+ form.AddItem(lst.GetMailmanFooter())
+
+def FormatOptionsSection(category, lst):
+ """Produce the category-specific options table."""
+ if category == 'members':
+ # Special case for members section.
+ return FormatMembershipOptions(lst)
+
+ options = GetConfigOptions(lst, category)
+
+ big_table = Table(cellspacing=3, cellpadding=4)
+
+ # Get and portray the text label for the category.
+ for k, v in CATEGORIES:
+ if k == category: label = v
+ big_table.AddRow([Center(Header(2, label))])
+ big_table.AddCellInfo(max(big_table.GetCurrentRowIndex(), 0), 0,
+ colspan=2, bgcolor="#99ccff")
+
+ def ColHeader(big_table = big_table):
+ big_table.AddRow([Center(Bold('Description')), Center(Bold('Value'))])
+ big_table.AddCellInfo(max(big_table.GetCurrentRowIndex(), 0), 0,
+ width="15%")
+ big_table.AddCellInfo(max(big_table.GetCurrentRowIndex(), 0), 1,
+ width="85%")
+ did_col_header = 0
+
+ for item in options:
+ if type(item) == types.StringType:
+ # 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...
+ if did_col_header:
+ item = "<center><i>" + item + "</i></center>"
+ big_table.AddRow([item])
+ big_table.AddCellInfo(max(big_table.GetCurrentRowIndex(), 0),
+ 0, colspan=2)
+ if not did_col_header:
+ # Do col header after very first string descr, if any...
+ ColHeader()
+ did_col_header = 1
+ else:
+ if not did_col_header:
+ # ... but do col header before anything else.
+ ColHeader()
+ did_col_header = 1
+ AddOptionsTableItem(big_table, item, category, lst)
+ big_table.AddRow(['<br>'])
+ big_table.AddCellInfo(big_table.GetCurrentRowIndex(), 0, colspan=2)
+ return big_table
+
+def AddOptionsTableItem(table, item, category, lst, nodetails=0):
+ """Add a row to an options table with the item description and value."""
+ try:
+ got = GetItemCharacteristics(item)
+ varname, kind, params, dependancies, descr, elaboration = got
+ except ValueError, msg:
+ lst.LogMsg("error", "admin: %s", msg)
+ return Italic("<malformed option>")
+ descr = GetItemGuiDescr(lst, category, varname, descr,
+ elaboration, nodetails)
+ val = GetItemGuiValue(lst, kind, varname, params)
+ table.AddRow([descr, val])
+ table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 1,
+ bgcolor="#cccccc")
+ table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 0,
+ bgcolor="#cccccc")
+
+def FormatOptionHelp(doc, varref, lst):
+ item = bad = None
+ reflist = string.split(varref, '/')
+ if len(reflist) == 2:
+ category, varname = reflist
+ options = GetConfigOptions(lst, category)
+ for i in options:
+ if i and i[0] == varname:
+ item = i
+ break
+ if not item:
+ bad = ("Option %s/%s not found. %s"
+ % (category, varname, os.environ['PATH_INFO']))
+ else:
+ try:
+ got = GetItemCharacteristics(item)
+ varname, kind, params, dependancies, descr, elaboration = got
+ except ValueError, msg:
+ bad = msg
+ if not bad and not elaboration:
+ bad = "Option %s has no extended help." % varname
+ if bad:
+ AddErrorMessage(doc, bad)
+ return
+
+ header = Table(width="100%")
+ legend = ('%s Maillist Configuration Help<br><em>%s</em> Option'
+ % (lst.real_name, varname))
+ header.AddRow([Center(Header(3, legend))])
+ header.AddCellInfo(max(header.GetCurrentRowIndex(), 0), 0,
+ colspan=2, bgcolor="#99ccff")
+ doc.SetTitle("Mailman %s List Option Help" % varname)
+ doc.AddItem(header)
+ doc.AddItem("<b>%s</b> (%s): %s<p>" % (varname, category, item[4]))
+ doc.AddItem("%s<p>" % item[5])
+
+ form = Form(os.path.join(lst.GetRelativeScriptURL('admin'), category))
+ valtab = Table(cellspacing=3, cellpadding=4)
+ AddOptionsTableItem(valtab, item, category, lst, nodetails=1)
+ form.AddItem(valtab)
+ # XXX I don't think we want to be able to set options from two places,
+ # since they'll go out of sync.
+ #form.AddItem(Center(FormatPasswordStuff()))
+ doc.AddItem(Center(form))
+
+def GetItemCharacteristics(table_entry):
+ """Break out the components of an item description from its table entry:
+ 0 option-var name
+ 1 type
+ 2 entry size
+ 3 ?dependancies?
+ 4 Brief description
+ 5 Optional description elaboration"""
+ if len(table_entry) == 5:
+ elaboration = None
+ varname, kind, params, dependancies, descr = table_entry
+ elif len(table_entry) == 6:
+ varname, kind, params, dependancies, descr, elaboration = table_entry
+ else:
+ raise ValueError, ("Badly formed options entry:\n %s"
+ % table_entry)
+ return (varname, kind, params, dependancies, descr, elaboration)
+
+def GetItemGuiValue(lst, kind, varname, params):
+ """Return a representation of an item's settings."""
+ if kind == mm_cfg.Radio or kind == mm_cfg.Toggle:
+ return RadioButtonArray(varname, params, getattr(lst, varname))
+ elif (kind == mm_cfg.String or kind == mm_cfg.Email or
+ kind == mm_cfg.Host or kind == mm_cfg.Number):
+ return TextBox(varname, getattr(lst, varname), params)
+ elif kind == mm_cfg.Text:
+ if params:
+ r, c = params
+ else:
+ r, c = None, None
+ val = getattr(lst, varname)
+ if not val:
+ val = ''
+ return TextArea(varname, val, r, c)
+ elif kind == mm_cfg.EmailList:
+ if params:
+ r, c = params
+ else:
+ r, c = None, None
+ res = string.join(getattr(lst, varname), '\n')
+ return TextArea(varname, res, r, c, wrap='off')
+
+def GetItemGuiDescr(lst, category, varname, descr, elaboration, nodetails):
+ """Return a representation of an item's description, with link to
+ elaboration if any."""
+ descr = '<div ALIGN="right">' + descr
+ if not nodetails and elaboration:
+ ref = "../" * (mm_utils.GetNestingLevel()-1) + list_name + "/"
+ ref = ref + '?VARHELP=' + category + "/" + varname
+ descr = Container(descr,
+ Link(ref, " (Details)", target="MMHelp"),
+ "</div>")
+ else:
+ descr = descr + "</div>"
+ return descr
+
+def FormatMembershipOptions(lst):
+ container = Container()
+ header = Table(width="100%")
+ header.AddRow([Center(Header(2, "Membership Management"))])
+ header.AddCellInfo(max(header.GetCurrentRowIndex(), 0), 0,
+ colspan=2, bgcolor="#99ccff")
+ container.AddItem(header)
+ user_table = Table(width="90%")
+ user_table.AddRow([Center(Header(4, "Membership List"))])
+ user_table.AddCellInfo(user_table.GetCurrentRowIndex(),
+ user_table.GetCurrentCellIndex(),
+ bgcolor="#cccccc", colspan=8)
+
+ members = {}
+ digests = {}
+ for member in lst.members:
+ members[member] = 1
+ for member in lst.digest_members:
+ digests[member] = 1
+ all = lst.members + lst.digest_members
+ if len(all) > mm_cfg.ADMIN_MEMBER_CHUNKSIZE:
+ chunks = mm_utils.chunkify(all)
+ if not cgi_data.has_key("chunk"):
+ chunk = 0
+ else:
+ chunk = string.atoi(cgi_data["chunk"].value)
+ all = chunks[chunk]
+ footer = ("<p><em>To View other sections, "
+ "click on the appropriate range listed below</em>")
+ chunk_indices = range(len(chunks))
+ chunk_indices.remove(chunk)
+ buttons = []
+ pi = os.environ["PATH_INFO"]
+ for ci in chunk_indices:
+ start, end = chunks[ci][0], chunks[ci][-1]
+ buttons.append("<a href=/mailman/admin%s?chunk=%d> from %s to %s </a>" % \
+ ( pi, ci, start, end))
+ buttons = apply(UnorderedList, tuple(buttons))
+ footer = footer + buttons.Format() + "<p>"
+ else:
+ all.sort()
+ footer = "<p>"
+ for member in all:
+ cells = [member + "<input type=hidden name=user value=%s>" % (member),
+ "subscribed " +CheckBox(member + "_subscribed", "on", 1).Format(),
+ ]
+ if members.get(member):
+ cells.append("digest " + CheckBox(member + "_digest", "off", 0).Format())
+ else:
+ cells.append("digest " + CheckBox(member + "_digest", "on", 1).Format())
+ for opt in ("hide", "nomail", "ack", "norcv", "plain"):
+ if lst.GetUserOption(member, mm_mailcmd.option_info[opt]):
+ value = "on"
+ checked = 1
+ else:
+ value = "off"
+ checked = 0
+ box = CheckBox("%s_%s" % (member, opt), value, checked)
+ cells.append("%s %s" % (opt, box.Format()))
+ user_table.AddRow(cells)
+ container.AddItem(Center(user_table))
+ container.AddItem(footer)
+ t = Table(width="90%")
+ t.AddRow([Center(Header(4, "Mass Subscribe Members"))])
+ t.AddCellInfo(t.GetCurrentRowIndex(),
+ t.GetCurrentCellIndex(),
+ bgcolor="#cccccc", colspan=8)
+ container.AddItem(Center(t))
+ container.AddItem(Center(TextArea(name='subscribees', rows=10,cols=60,wrap=None)))
+ container.AddItem(Center("<em> Enter One address per line</em><p>"))
+ return container
+
+def FormatPasswordStuff():
+ submit = Table(bgcolor="#99ccff",
+ border=0, cellspacing=0, cellpadding=2, width="100%")
+ submit.AddRow([Bold(SubmitButton('submit', 'Submit Your Changes'))])
+ submit.AddCellInfo(submit.GetCurrentRowIndex(), 0, align="middle")
+ change_pw_table = Table(bgcolor="#99cccc", border=0,
+ cellspacing=0, cellpadding=2, width="90%")
+ change_pw_table.AddRow([Bold(Center('To Change The Administrator Password')),
+ '<div ALIGN="right"> Enter the new password: </div>',
+ PasswordBox('newpw'),])
+ change_pw_table.AddCellInfo(0, 0, align="middle", colspan=2)
+ change_pw_table.AddRow(['<div ALIGN="right"> Enter the current password </div>',
+ PasswordBox('adminpw'),
+ '<div ALIGN="right">Again to confirm it:</div>',
+ PasswordBox('confirmpw')])
+ password_stuff = Container()
+ password_stuff.AddItem(change_pw_table)
+ password_stuff.AddItem("<p>")
+ password_stuff.AddItem(submit)
+ return password_stuff
+
+# XXX klm - looks like turn_on_moderation is orphaned.
+turn_on_moderation = 0
+
+# Options processing
+
+def GetValidValue(lst, prop, my_type, val, dependant):
+ if my_type == mm_cfg.Radio or my_type == mm_cfg.Toggle:
+ if type(val) <> types.IntType:
+ try:
+ # XXX Security!?
+ val = eval(val)
+ except:
+ pass
+ # Don't know what to do here...
+ return val
+ elif my_type == mm_cfg.String or my_type == mm_cfg.Text:
+ return val
+ elif my_type == mm_cfg.Email:
+ try:
+ valid = mm_utils.ValidEmail(val)
+ if valid:
+ return val
+ except:
+ pass
+ # Revert to the old value.
+ return getattr(lst, prop)
+ elif my_type == mm_cfg.EmailList:
+ def SafeValidAddr(addr):
+ import mm_utils
+ try:
+ valid = mm_utils.ValidEmail(addr)
+ if valid:
+ return 1
+ else:
+ return 0
+ except:
+ return 0
+
+ val = filter(SafeValidAddr,
+ map(string.strip, string.split(val, '\n')))
+ if dependant and len(val):
+ # Wait till we've set everything to turn it on,
+ # as we don't want to clobber our special case.
+ # XXX klm - looks like turn_on_moderation is orphaned?
+ turn_on_moderation = 1
+ return val
+ elif my_type == mm_cfg.Host:
+ return val
+##
+## This code is sendmail dependant, so we'll just live w/o
+## the error checking for now.
+##
+## # Shouldn't have to read in the whole file.
+## file = open('/etc/sendmail.cf', 'r')
+## lines = string.split(file.read(), '\n')
+## file.close()
+## def ConfirmCWEntry(item):
+## return item[0:2] == 'Cw'
+## lines = filter(ConfirmCWEntry, lines)
+## if not len(lines):
+## # Revert to the old value.
+## return getattr(list, prop)
+## for line in lines:
+## if string.lower(string.strip(line[2:])) == string.lower(val):
+## return val
+## return getattr(list, prop)
+ elif my_type == mm_cfg.Number:
+ try:
+ num = eval(val)
+ if num < 0:
+ return getattr(lst, prop)
+ return num
+ except:
+ return getattr(lst, prop)
+ else:
+ # Should never get here...
+ return val
+
+
+def ChangeOptions(lst, category, cgi_info, document):
+ dirty = 0
+ confirmed = 0
+ if cgi_info.has_key('newpw'):
+ if cgi_info.has_key('confirmpw'):
+ if cgi_info.has_key('adminpw'):
+ try:
+ lst.ConfirmAdminPassword(cgi_info['adminpw'].value)
+ confirmed = 1
+ except mm_err.MMBadPasswordError:
+ m = "Error: incorrect administrator password"
+ document.AddItem(Header(3, Italic(FontAttr(m, color="ff5060"))))
+ confirmed = 0
+ if confirmed:
+ new = cgi_info['newpw'].value
+ confirm = cgi_info['confirmpw'].value
+ if new == confirm:
+ lst.password = crypt.crypt(new, mm_utils.GetRandomSeed())
+ dirty = 1
+ else:
+ m = 'Error: Passwords did not match.'
+ document.AddItem(Header(3, Italic(FontAttr(m, color="ff5060"))))
+
+ else:
+ m = 'Error: You must type in your new password twice.'
+ document.AddItem(
+ Header(3, Italic(FontAttr(m, color="ff5060"))))
+ #
+ # for some reason, the login page mangles important values for the list
+ # such as .real_name so we only process these changes if the category
+ # is not "members" and the request is not from the login page
+ # -scott 19980515
+ #
+ if category != 'members' and not cgi_info.has_key("request_login") and\
+ len(cgi_info.keys()) > 1:
+ opt_list = GetConfigOptions(lst, category)
+ for item in opt_list:
+ if len(item) < 5:
+ continue
+ property, kind, args, deps, desc = (item[0], item[1], item[2],
+ item[3], item[4])
+ if not cgi_info.has_key(property):
+ if (kind <> mm_cfg.Text and
+ kind <> mm_cfg.String and
+ kind <> mm_cfg.EmailList):
+ continue
+ else:
+ val = ''
+ else:
+ val = cgi_info[property].value
+ value = GetValidValue(lst, property, kind, val, deps)
+ if getattr(lst, property) != value:
+ setattr(lst, property, value)
+ dirty = 1
+ #
+ # mass subscription processing for members category
+ #
+ if cgi_info.has_key('subscribees'):
+ name_text = cgi_info['subscribees'].value
+ name_text = string.replace(name_text, '\r', '')
+ names = string.split(name_text, '\n')
+ if '' in names:
+ names.remove('')
+ subscribe_success = []
+ subscribe_errors = []
+ for new_name in map(string.strip,names):
+ digest = 0
+ if not lst.digestable:
+ digest = 0
+ if not lst.nondigestable:
+ digest = 1
+ try:
+ lst.ApprovedAddMember(new_name, (mm_utils.GetRandomSeed() +
+ mm_utils.GetRandomSeed()), digest)
+ subscribe_success.append(new_name)
+ except mm_err.MMAlreadyAMember:
+ subscribe_errors.append((new_name, 'Already a member'))
+
+ except mm_err.MMBadEmailError:
+ subscribe_errors.append((new_name, "Bad/Invalid email address"))
+ except mm_err.MMHostileAddress:
+ subscribe_errors.append((new_name, "Hostile Address (illegal characters)"))
+ if subscribe_success:
+ document.AddItem(Header(5, "Successfully Subscribed:"))
+ document.AddItem(apply(UnorderedList, tuple((subscribe_success))))
+ document.AddItem("<p>")
+ if subscribe_errors:
+ document.AddItem(Header(5, "Error Subscribing:"))
+ items = map(lambda x: "%s -- %s" % (x[0], x[1]), subscribe_errors)
+ document.AddItem(apply(UnorderedList, tuple((items))))
+ document.AddItem("<p>")
+ #
+ # do the user options for members category
+ #
+ if cgi_info.has_key('user'):
+ user = cgi_info["user"]
+ if type(user) is type([]):
+ users = []
+ for ui in range(len(user)):
+ users.append(user[ui].value)
+ else:
+ users = [user.value]
+ for user in users:
+ if not cgi_info.has_key('%s_subscribed' % (user)):
+ lst.DeleteMember(user)
+ dirty = 1
+ continue
+ if not cgi_info.has_key("%s_digest" % (user)):
+ if user in lst.digest_members:
+ list.digest_members.remove(user)
+ dirty = 1
+ if user not in lst.members:
+ lst.members.append(user)
+ dirty = 1
+ else:
+ if user not in lst.digest_members:
+ lst.digest_members.append(user)
+ dirty = 1
+ if user in lst.members:
+ lst.members.remove(user)
+ dirty = 1
+
+ for opt in ("hide", "nomail", "ack", "norcv", "plain"):
+ if cgi_info.has_key("%s_%s" % (user, opt)):
+ lst.SetUserOption(user, mm_mailcmd.option_info[opt], 1)
+ dirty = 1
+ else:
+ lst.SetUserOption(user, mm_mailcmd.option_info[opt], 0)
+ dirty = 1
+
+
+ if dirty:
+ lst.Save()
+
+def AddErrorMessage(doc, errmsg, *args):
+ doc.AddItem(Header(3, Italic(FontAttr(errmsg % args,
+ color="#ff66cc"))))
+
+
+_config_info = None
+def GetConfigOptions(lst, category):
+ global _config_info
+ if _config_info == None:
+ _config_info = lst.GetConfigInfo()
+ return _config_info[category]
+