#!/usr/local/bin/python """Produce the list-administration web page on stdout. To run stand-alone for debugging, set env var PATH_INFO to name of list.""" import sys sys.path.append('/home/mailman/mailman/modules') import os, cgi, string, crypt, types import mm_utils, maillist, mm_cfg, htmlformat, mm_err try: sys.stderr = mm_utils.StampedLogger("error", label = 'mmroster', manual_reprime=1, nofail=0) except IOError: pass # Oh well - SOL on redirect, errors show thru. def main(): global doc, list_name # For encompassing try/except. doc = htmlformat.Document() path = os.environ['PATH_INFO'] list_info = mm_utils.GetPathPieces(path) if len(list_info) < 1: doc.AddItem(htmlformat.Header(2, "Invalid options to CGI script.")) print doc.Format() sys.exit(0) list_name = string.lower(list_info[0]) list = maillist.MailList(list_name) try: if not (list and list._ready): doc.AddItem(htmlformat.Header(3, "%s: No such list" % list_name)) print doc.Format() sys.exit(0) info = list.GetConfigInfo() general = info['general'] nodigest = info['nondigest'] digest = info['digest'] archives = info['archive'] # XXX klm - looks like john distinguished some special '.jp' lists if list._internal_name[-3:] <> '.jp': bounce = info['bounce'] else: bounce = [] cgi_data = cgi.FieldStorage() if len(cgi_data.keys()): if not cgi_data.has_key('adminpw'): doc.AddItem('
') m = ('Error: You must supply the admin password to ' 'change options.') doc.AddItem( htmlformat.Header(3, htmlformat.Italic( htmlformat.FontAttr( m, color="ff5060")))) else: try: list.ConfirmAdminPassword(cgi_data['adminpw'].value) ChangeOptions(list, (general + nodigest + digest + archives + bounce), cgi_data, doc) # Yuck. This shouldn't need to be here. if not list.digestable and not list.nondigestable: list.nondigestable = 1 except mm_err.MMBadPasswordError: doc.AddItem('
') m = 'Error: Incorrect admin password.' doc.AddItem( htmlformat.Header(3, htmlformat.Italic( htmlformat.FontAttr( m, color="ff5060")))) if not list.digestable and len(list.digest_members): doc.AddItem('
') doc.AddItem( htmlformat.Header(3, 'Warning: you have digest members, ' 'but digests are turned off. ' 'Those people will not receive mail.')) if not list.nondigestable and len(list.members): doc.AddItem('
') doc.AddItem( htmlformat.Header(3, 'Warning: you have list members, ' 'but non-digestified mail is turned ' 'off. They will receive mail until ' 'you fix this problem.')) if len(cgi_data.keys()): if (cgi_data.has_key('bounce_matching_headers')): try: pairs = list.parse_matching_header_opt() except mm_err.MMBadConfigError, line: doc.AddItem('
') m = ('Warning: bad matching-header line' '(does it have the colon?)' % line) doc.AddItem( htmlformat.Header(3, htmlformat.Italic( htmlformat.FontAttr( m, color="ff5060")))) FormatConfiguration(doc, list) print doc.Format() finally: list.Unlock() def GetGuiItem(table_entry, list): varname, type, params, dependancies, descr = table_entry if type == mm_cfg.Radio or type == mm_cfg.Toggle: gui_part = htmlformat.RadioButtonArray(varname, params, getattr(list, varname)) elif (type == mm_cfg.String or type == mm_cfg.Email or type == mm_cfg.Host or type == mm_cfg.Number): gui_part = htmlformat.TextBox(varname, getattr(list, varname), params) elif type == mm_cfg.Text: if params: r, c = params else: r, c = None, None val = getattr(list, varname) if not val: val = '' gui_part = htmlformat.TextArea(varname, val, r, c) elif type == mm_cfg.EmailList: if params: r, c = params else: r, c = None, None res = string.join(getattr(list, varname), '\n') gui_part = htmlformat.TextArea(varname, res, r, c, wrap='off') return gui_part def FormatConfiguration(doc, list): info = list.GetConfigInfo() general = info['general'] nodigest = info['nondigest'] digest = info['digest'] archives = info['archive'] # XXX klm - looks like john distinguished some special '.jp' lists if list._internal_name[-3:] <> '.jp': bounce = info['bounce'] else: bounce = [] doc.SetTitle('%s Administration' % list.real_name) doc.AddItem(htmlformat.Header(2, ('Configuration for %s' % list.real_name))) doc.AddItem('
') links = htmlformat.UnorderedList() link = htmlformat.Link(list.GetScriptURL('admindb'), 'View or edit the administrative ' 'requests database.') link = htmlformat.FontAttr(link, size="+1") links.AddItem(link) link = htmlformat.Link(list.GetScriptURL('listinfo'), 'Go to the general list information page.') link = htmlformat.FontAttr(link, size="+1") links.AddItem(link) link = htmlformat.Link(list.GetScriptURL('edithtml'), 'Edit the HTML for the public list pages.') link = htmlformat.FontAttr(link, size="+1") links.AddItem(link) doc.AddItem(links) doc.AddItem('
') form = htmlformat.Form(list.GetScriptURL('admin')) doc.AddItem(form) password_submit = htmlformat.Table() password_submit.AddRow([htmlformat.Bold('For changes, enter the ' 'admin password:'), htmlformat.PasswordBox('adminpw')]) rdy = htmlformat.Center(htmlformat.Bold("And when you're ready... ")) password_submit.AddRow([rdy, htmlformat.FontAttr( htmlformat.SubmitButton('submit', 'Submit Changes'), color='red'),]) change_pw_table = htmlformat.Table() change_pw_table.AddRow( [htmlformat.Underline(htmlformat.Center('Change Your Password'))]) change_pw_table.AddCellInfo(change_pw_table.GetCurrentRowIndex(), 0, colspan=2) change_pw_table.AddRow(['Enter your new password:', htmlformat.PasswordBox('newpw')]) change_pw_table.AddRow(['And also confirm it:', htmlformat.PasswordBox('confirmpw')]) password_stuff = htmlformat.Table(border=1) password_stuff.AddRow([password_submit, change_pw_table]) form.AddItem(password_stuff) big_table = htmlformat.Table(border=1) FormatOptionsSection('General Options', general, big_table, list) FormatOptionsSection('Non-Digest Options', nodigest, big_table, list) FormatOptionsSection('Digest Options', digest, big_table, list) FormatOptionsSection('Bounce Administration Options', bounce, big_table, list) FormatOptionsSection('Archival Options', archives, big_table, list) form.AddItem(big_table) # Improve the html here... form.AddItem('

Mass Subscribe Members

') form.AddItem('

Enter one email address per line

') form.AddItem(htmlformat.TextArea(name='subscribees', rows=20,cols=60,wrap=None)) form.AddItem('

To Unsubscribe Members...

') form.AddItem(""" The key to unsubscribing members is the fact that you can use your admin password in place of the user's password to change user options or unsubscribe them. Visit the user in question's options page (from the listinfo page) and do the unsubscribe procedure, providing the admin password instead of the user's privacy password.

Note that you can also use the subscriber option to inhibit their delivery of messages, if you figure the bounces are transient.""" % list.GetScriptURL('listinfo')) form.AddItem(list.GetMailmanFooter()) def FormatOptionsSection(name, options, big_table, list): big_table.AddRow([htmlformat.Center( htmlformat.Header(2, name))]) big_table.AddCellInfo(max(big_table.GetCurrentRowIndex(), 0), 0, colspan=2) big_table.AddRow([htmlformat.Bold('Option'), htmlformat.Bold('Value')]) item = options[0] gui_item = GetGuiItem(item, list) big_table.AddRow([item[4], gui_item]) if len(options) > 1: if options[0][1] == mm_cfg.Toggle: big_table.AddRow([htmlformat.Header(2, "If so:")]) big_table.AddCellInfo(big_table.GetCurrentRowIndex(), 0, colspan=2) for item in options[1:]: gui_item = GetGuiItem(item, list) big_table.AddRow([item[4], gui_item]) big_table.AddRow(['
']) big_table.AddCellInfo(big_table.GetCurrentRowIndex(), 0, colspan=2) # XXX klm - looks like turn_on_moderation is orphaned. turn_on_moderation = 0 def GetValidValue(list, prop, my_type, val, dependant): if my_type == mm_cfg.Radio or my_type == mm_cfg.Toggle: if type(val) <> types.IntType: try: 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(list, 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(list, prop) return num except: return getattr(list, prop) else: # Should never get here... return val def ChangeOptions(list, opt_list, cgi_info, document): for item in opt_list: property, type, args, dependancies, desc = item if not cgi_info.has_key(property): if (type <> mm_cfg.Text and type <> mm_cfg.String and type <> mm_cfg.EmailList): continue else: val = '' else: val = cgi_info[property].value value = GetValidValue(list, property, type, val, dependancies) setattr(list, property, value) if cgi_info.has_key('subscribees'): name_text = cgi_info['subscribees'].value names = string.split(name_text, '\r\n') for new_name in names: try: #FIXME: The admin needs to be able to specify subscribe options list.AddMember(new_name, (mm_utils.GetRandomSeed() + mm_utils.GetRandomSeed())) #FIXME: Give some sort of an indication of which names didn't work, # and why they didn't work... except: pass if cgi_info.has_key('newpw'): if cgi_info.has_key('confirmpw'): new = cgi_info['newpw'].value confirm = cgi_info['confirmpw'].value if new == confirm: list.password = crypt.crypt(new, mm_utils.GetRandomSeed()) else: m = 'Error: Passwords did not match.' document.AddItem( htmlformat.Header(3, htmlformat.Italic( htmlformat.FontAttr( m, color="ff5060")))) else: m = 'Error: You must type in your new password twice.' document.AddItem( htmlformat.Header(3, htmlformat.Italic( htmlformat.FontAttr( m, color="ff5060")))) list.Save() def error_page(errmsg, *args): print apply(error_page_doc, (errmsg,) + args).Format() def error_page_doc(errmsg, *args): """Produce a simple error-message page on stdout and exit. Optional arg justreturn means just return the doc, don't print it.""" doc = htmlformat.Document() doc.AddItem(htmlformat.Header(2, "Error")) doc.AddItem(htmlformat.Bold(errmsg % args)) return doc if __name__ == "__main__": try: main() except KeyboardInterrupt: print "Interrupted!" raise SystemExit, 0 except mm_err.MMUnknownListError, msg: error_page("%s: %s", list_name, msg) raise SystemExit, 0 except: print "Content-type: text/html\n" print "

We're sorry, we hit a bug!

\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
"
	try:
	    import traceback
	    sys.stderr = sys.stdout
	    traceback.print_exc()
	except:
	    print "[failed to get traceback]"
	print "\n\n
"