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')), ('masthead.txt', _('Digest masthead')), ('postheld.txt', _('User notice of held post')), ('approve.txt', _('User notice of held subscription')), ('refuse.txt', _('Notice of post refused by moderator')), ('invite.txt', _('Invitation to join list')), ('verify.txt', _('Request to confirm subscription')), ('unsub.txt', _('Request to confirm unsubscription')), ('nomoretoday.txt', _('User notice of autoresponse limit')), ('postack.txt', _('User post acknowledgement')), ('disabled.txt', _('Subscription disabled by bounce warning')), ('admlogin.html', _('Admin/moderator login page')), ('private.html', _('Private archive login page')), ('userpass.txt', _('On demand password reminder')), ) _ = i18n._ doc = Document() # Set up the system default language i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) doc.set_language(mm_cfg.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 as e: # Avoid cross-site scripting attacks safelistname = Utils.websafe(listname) doc.AddItem(Header(2, _('No such list <em>%(safelistname)s</em>'))) # Send this with a 404 status. print('Status: 404 Not Found') print(doc.Format()) syslog('error', 'edithtml: 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() try: cgidata.getfirst('adminpw', '') except TypeError: # Someone crafted a POST with a bad Content-Type:. doc.AddItem(Header(2, _("Error"))) doc.AddItem(Bold(_('Invalid options to CGI script.'))) # Send this with a 400 status. print('Status: 400 Bad Request') print(doc.Format()) return # CSRF check safe_params = ['VARHELP', 'adminpw', 'admlogin'] params = list(cgidata.keys()) if set(params) - set(safe_params): csrf_checked = csrf_check(mlist, cgidata.getfirst('csrf_token')) else: csrf_checked = True # if password is present, void cookie to force password authentication. if cgidata.getfirst('adminpw'): os.environ['HTTP_COOKIE'] = '' csrf_checked = True # Editing the html for a list is limited to the list admin and site admin. if not mlist.WebAuthenticate((mm_cfg.AuthListAdmin, mm_cfg.AuthSiteAdmin), cgidata.getfirst('adminpw', '')): if 'admlogin' in cgidata: # This is a re-authorization attempt msg = Bold(FontSize('+1', _('Authorization failed.'))).Format() remote = os.environ.get( 'HTTP_FORWARDED_FOR', os.environ.get( 'HTTP_X_FORWARDED_FOR', os.environ.get('REMOTE_ADDR', 'unidentified origin'))) syslog('security', 'Authorization failed (edithtml): list=%s: remote=%s', listname, remote) 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 list(cgidata.keys()): if csrf_checked: ChangeHTML(mlist, cgidata, template_name, doc) else: doc.addError( _('The form lifetime has expired. (request forgery check)') ) FormatHTML(mlist, doc, template_name, template_info) finally: doc.AddItem(mlist.GetMailmanFooter()) print(doc.Format())
# 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((mm_cfg.AuthListAdmin, mm_cfg.AuthListModerator, mm_cfg.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 # Add logout function. Note that admindb may be accessed with # site-wide admin, moderator and list admin privileges. # site admin may have site or admin cookie. (or both?) # See if this is a logout request if len(parts) >= 2 and parts[1] == 'logout': if mlist.AuthContextInfo(mm_cfg.AuthSiteAdmin)[0] == 'site': print mlist.ZapCookie(mm_cfg.AuthSiteAdmin) if mlist.AuthContextInfo(mm_cfg.AuthListModerator)[0]: print mlist.ZapCookie(mm_cfg.AuthListModerator) print mlist.ZapCookie(mm_cfg.AuthListAdmin) Auth.loginpage(mlist, 'admindb', frontpage=1) return
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((mm_cfg.AuthListAdmin, mm_cfg.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'))
# 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( (mm_cfg.AuthListAdmin, mm_cfg.AuthListModerator, mm_cfg.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 # Add logout function. Note that admindb may be accessed with # site-wide admin, moderator and list admin privileges. # site admin may have site or admin cookie. (or both?) # See if this is a logout request if len(parts) >= 2 and parts[1] == 'logout': if mlist.AuthContextInfo(mm_cfg.AuthSiteAdmin)[0] == 'site': print mlist.ZapCookie(mm_cfg.AuthSiteAdmin) if mlist.AuthContextInfo(mm_cfg.AuthListModerator)[0]: print mlist.ZapCookie(mm_cfg.AuthListModerator) print mlist.ZapCookie(mm_cfg.AuthListAdmin) Auth.loginpage(mlist, 'admindb', frontpage=1) return
# now that we have the list name, create the list object try: mlist = MailList.MailList(listname, lock=0) except Errors.MMListError, e: # Avoid cross-site scripting attack safelistname = Utils.QuoteHyperChars(listname) handle_no_list(doc, 'No such list <em>%s</em><p>' % safelistname) syslog('error', 'No such list "%s": %s\n' % (listname, e)) return # # now we must authorize the user to view this page, and if they are, to # handle both the printing of the current outstanding requests, and the # selected actions cgidata = cgi.FieldStorage() try: Auth.authenticate(mlist, cgidata) except Auth.NotLoggedInError, e: Auth.loginpage(mlist, 'admindb', e.message) return # We need a signal handler to catch the SIGTERM that can come from Apache # when the user hits the browser's STOP button. See the comment in # admin.py for details. # # BAW: Strictly speaking, the list should not need to be locked just to # read the request database. However the request database asserts that # the list is locked in order to load it and it's not worth complicating # that logic. def sigterm_handler(signum, frame, mlist=mlist): # Make sure the list gets unlocked... mlist.Unlock()
FormatAdminOverview('No such list <em>%s</em>' % safelistname) syslog('error', 'Someone tried to access the admin interface for a ' 'non-existent list: %s' % listname) return if len(parts) == 1: category = 'general' category_suffix = '' else: category = parts[1] category_suffix = category # If the user is not authenticated, we're done. cgidata = cgi.FieldStorage(keep_blank_values=1) try: Auth.authenticate(mlist, cgidata) except Auth.NotLoggedInError, e: Auth.loginpage(mlist, 'admin', e.message) return # Is this a log-out request? if category == 'logout': print mlist.ZapCookie('admin') Auth.loginpage(mlist, 'admin', frontpage=1) return if category not in map(lambda x: x[0], CATEGORIES): category = 'general' # is the request for variable details? varhelp = None
def main(): global ssort # 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 as e: # Avoid cross-site scripting attacks safelistname = Utils.websafe(listname) # Send this with a 404 status. print('Status: 404 Not Found') handle_no_list(_('No such list <em>%(safelistname)s</em>')) syslog('error', 'admindb: 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) try: cgidata.getfirst('adminpw', '') except TypeError: # Someone crafted a POST with a bad Content-Type:. doc = Document() doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE) doc.AddItem(Header(2, _("Error"))) doc.AddItem(Bold(_('Invalid options to CGI script.'))) # Send this with a 400 status. print('Status: 400 Bad Request') print(doc.Format()) return # CSRF check safe_params = ['adminpw', 'admlogin', 'msgid', 'sender', 'details'] params = list(cgidata.keys()) if set(params) - set(safe_params): csrf_checked = csrf_check(mlist, cgidata.getfirst('csrf_token')) else: csrf_checked = True # if password is present, void cookie to force password authentication. if cgidata.getfirst('adminpw'): os.environ['HTTP_COOKIE'] = '' csrf_checked = True if not mlist.WebAuthenticate((mm_cfg.AuthListAdmin, mm_cfg.AuthListModerator, mm_cfg.AuthSiteAdmin), cgidata.getfirst('adminpw', '')): if 'adminpw' in cgidata: # This is a re-authorization attempt msg = Bold(FontSize('+1', _('Authorization failed.'))).Format() remote = os.environ.get('HTTP_FORWARDED_FOR', os.environ.get('HTTP_X_FORWARDED_FOR', os.environ.get('REMOTE_ADDR', 'unidentified origin'))) syslog('security', 'Authorization failed (admindb): list=%s: remote=%s', listname, remote) else: msg = '' Auth.loginpage(mlist, 'admindb', msg=msg) return # Add logout function. Note that admindb may be accessed with # site-wide admin, moderator and list admin privileges. # site admin may have site or admin cookie. (or both?) # See if this is a logout request if len(parts) >= 2 and parts[1] == 'logout': if mlist.AuthContextInfo(mm_cfg.AuthSiteAdmin)[0] == 'site': print(mlist.ZapCookie(mm_cfg.AuthSiteAdmin)) if mlist.AuthContextInfo(mm_cfg.AuthListModerator)[0]: print(mlist.ZapCookie(mm_cfg.AuthListModerator)) print(mlist.ZapCookie(mm_cfg.AuthListAdmin)) Auth.loginpage(mlist, 'admindb', frontpage=1) 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] # We need a signal handler to catch the SIGTERM that can come from Apache # when the user hits the browser's STOP button. See the comment in # admin.py for details. # # BAW: Strictly speaking, the list should not need to be locked just to # read the request database. However the request database asserts that # the list is locked in order to load it and it's not worth complicating # that logic. 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) mlist.Lock() try: # Install the emergency shutdown signal handler signal.signal(signal.SIGTERM, sigterm_handler) realname = mlist.real_name if not list(cgidata.keys()) or 'admlogin' in cgidata: # 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')) if csrf_checked: process_form(mlist, doc, cgidata) else: doc.addError( _('The form lifetime has expired. (request forgery check)')) # Now print the results and we're done. Short circuit for when there # are no pending requests, but be sure to save the results! admindburl = mlist.GetScriptURL('admindb', absolute=1) if not mlist.NumRequestsPending(): 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(admindburl, _('Click here to reload this page.'))) # Put 'Logout' link before the footer doc.AddItem('\n<div align="right"><font size="+2">') doc.AddItem(Link('%s/logout' % admindburl, '<b>%s</b>' % _('Logout'))) doc.AddItem('</font></div>\n') doc.AddItem(mlist.GetMailmanFooter()) print(doc.Format()) mlist.Save() return form = Form(admindburl, mlist=mlist, contexts=AUTH_CONTEXTS) # 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')))) nomessages = not mlist.GetHeldMessageIds() if not (details or sender or msgid or nomessages): form.AddItem(Center( '<label>' + CheckBox('discardalldefersp', 0).Format() + ' ' + _('Discard all messages marked <em>Defer</em>') + '</label>' )) # Add a link back to the overview, if we're not viewing the overview! adminurl = mlist.GetScriptURL('admin', absolute=1) 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, ssort) 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( '<label>' + CheckBox('discardalldefersp', 0).Format() + ' ' + _('Discard all messages marked <em>Defer</em>') + '</label>' )) form.AddItem(Center(SubmitButton('submit', _('Submit All Data')))) # Put 'Logout' link before the footer doc.AddItem('\n<div align="right"><font size="+2">') doc.AddItem(Link('%s/logout' % admindburl, '<b>%s</b>' % _('Logout'))) doc.AddItem('</font></div>\n') doc.AddItem(mlist.GetMailmanFooter()) print(doc.Format()) # Commit all changes mlist.Save() finally: mlist.Unlock()