1 # Copyright (C) 1998-2016 by the Free Software Foundation, Inc.
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
18 """Produce listinfo page, primary web entry-point to mailing lists.
21 # No lock needed in this script, because we don't change data.
27 from Mailman import mm_cfg
28 from Mailman import Utils
29 from Mailman import Captcha # MAILMAN_CAPTCHA_PATCHED
30 from Mailman import MailList
31 from Mailman import Errors
32 from Mailman import i18n
33 from Mailman.htmlformat import *
34 from Mailman.Logging.Syslog import syslog
38 i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
44 parts = Utils.GetPathPieces()
49 listname = parts[0].lower()
51 mlist = MailList.MailList(listname, lock=0)
52 except Errors.MMListError, e:
53 # Avoid cross-site scripting attacks
54 safelistname = Utils.websafe(listname)
55 # Send this with a 404 status.
56 print 'Status: 404 Not Found'
57 listinfo_overview(_('No such list <em>%(safelistname)s</em>'))
58 syslog('error', 'listinfo: No such list "%s": %s', listname, e)
61 # See if the user want to see this page in other language
62 cgidata = cgi.FieldStorage()
64 language = cgidata.getvalue('language')
66 # Someone crafted a POST with a bad Content-Type:.
68 doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
69 doc.AddItem(Header(2, _("Error")))
70 doc.AddItem(Bold(_('Invalid options to CGI script.')))
71 # Send this with a 400 status.
72 print 'Status: 400 Bad Request'
76 if not Utils.IsLanguage(language):
77 language = mlist.preferred_language
78 i18n.set_language(language)
79 list_listinfo(mlist, language)
84 def listinfo_overview(msg=''):
85 # Present the general listinfo overview
86 hostname = Utils.get_domain()
87 # Set up the document and assign it the correct language. The only one we
88 # know about at the moment is the server's default.
90 doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
92 legend = _("%(hostname)s Mailing Lists")
95 table = Table(border=0, width="100%")
96 table.AddRow([Center(Header(2, legend))])
97 table.AddCellInfo(table.GetCurrentRowIndex(), 0, colspan=2,
98 bgcolor=mm_cfg.WEB_HEADER_COLOR)
100 # Skip any mailing lists that isn't advertised.
102 listnames = Utils.list_names()
105 for name in listnames:
107 mlist = MailList.MailList(name, lock=0)
108 except Errors.MMUnknownListError:
109 # The list could have been deleted by another process.
112 if mm_cfg.VIRTUAL_HOST_OVERVIEW and (
113 mlist.web_page_url.find('/%s/' % hostname) == -1 and
114 mlist.web_page_url.find('/%s:' % hostname) == -1):
115 # List is for different identity of this host - skip it.
118 advertised.append((mlist.GetScriptURL('listinfo'),
120 Utils.websafe(mlist.description)))
122 greeting = FontAttr(msg, color="ff5060", size="+1")
124 greeting = FontAttr(_('Welcome!'), size='+2')
127 mailmanlink = Link(mm_cfg.MAILMAN_URL, _('Mailman')).Format()
130 _('''<p>There currently are no publicly-advertised
131 %(mailmanlink)s mailing lists on %(hostname)s.'''))
134 _('''<p>Below is a listing of all the public mailing lists on
135 %(hostname)s. Click on a list name to get more information about
136 the list, or to subscribe, unsubscribe, and change the preferences
137 on your subscription.'''))
139 # set up some local variables
140 adj = msg and _('right') or ''
141 siteowner = Utils.get_site_email()
143 (_(''' To visit the general information page for an unadvertised list,
144 open a URL similar to this one, but with a '/' and the %(adj)s
146 <p>List administrators, you can visit '''),
147 Link(Utils.ScriptURL('admin'),
148 _('the list admin overview page')),
149 _(''' to find the management interface for your list.
150 <p>If you are having trouble using the lists, please contact '''),
151 Link('mailto:' + siteowner, siteowner),
154 table.AddRow([apply(Container, welcome)])
155 table.AddCellInfo(max(table.GetCurrentRowIndex(), 0), 0, colspan=2)
158 table.AddRow([' ', ' '])
159 table.AddRow([Bold(FontAttr(_('List'), size='+2')),
160 Bold(FontAttr(_('Description'), size='+2'))
163 for url, real_name, description in advertised:
165 [Link(url, Bold(real_name)),
166 description or Italic(_('[no description available]'))])
167 if highlight and mm_cfg.WEB_HIGHLIGHT_COLOR:
168 table.AddRowInfo(table.GetCurrentRowIndex(),
169 bgcolor=mm_cfg.WEB_HIGHLIGHT_COLOR)
170 highlight = not highlight
174 doc.AddItem(MailmanLogo())
180 def list_listinfo(mlist, lang):
181 # Generate list specific listinfo
182 doc = HeadlessDocument()
183 doc.set_language(lang)
185 replacements = mlist.GetStandardReplacements(lang)
187 if not mlist.digestable or not mlist.nondigestable:
188 replacements['<mm-digest-radio-button>'] = ""
189 replacements['<mm-undigest-radio-button>'] = ""
190 replacements['<mm-digest-question-start>'] = '<!-- '
191 replacements['<mm-digest-question-end>'] = ' -->'
193 replacements['<mm-digest-radio-button>'] = mlist.FormatDigestButton()
194 replacements['<mm-undigest-radio-button>'] = \
195 mlist.FormatUndigestButton()
196 replacements['<mm-digest-question-start>'] = ''
197 replacements['<mm-digest-question-end>'] = ''
198 replacements['<mm-plain-digests-button>'] = \
199 mlist.FormatPlainDigestsButton()
200 replacements['<mm-mime-digests-button>'] = mlist.FormatMimeDigestsButton()
201 replacements['<mm-subscribe-box>'] = mlist.FormatBox('email', size=30)
202 replacements['<mm-subscribe-button>'] = mlist.FormatButton(
203 'email-button', text=_('Subscribe'))
204 replacements['<mm-new-password-box>'] = mlist.FormatSecureBox('pw')
205 replacements['<mm-confirm-password>'] = mlist.FormatSecureBox('pw-conf')
206 replacements['<mm-subscribe-form-start>'] = mlist.FormatFormStart(
208 if mm_cfg.SUBSCRIBE_FORM_SECRET:
209 now = str(int(time.time()))
210 remote = os.environ.get('HTTP_FORWARDED_FOR',
211 os.environ.get('HTTP_X_FORWARDED_FOR',
212 os.environ.get('REMOTE_ADDR',
214 # Try to accept a range in case of load balancers, etc. (LP: #1447445)
215 if remote.find('.') >= 0:
216 # ipv4 - drop last octet
217 remote = remote.rsplit('.', 1)[0]
219 # ipv6 - drop last 16 (could end with :: in which case we just
220 # drop one : resulting in an invalid format, but it's only
221 # for our hash so it doesn't matter.
222 remote = remote.rsplit(':', 1)[0]
224 (captcha_question, captcha_box, captcha_idx) = Captcha.displayhtml(mlist, mm_cfg.CAPTCHAS)
225 replacements['<mm-captcha-question>'] = captcha_question
226 replacements['<mm-captcha-box>'] = captcha_box
228 replacements['<mm-subscribe-form-start>'] += (
229 '<input type="hidden" name="sub_form_token" value="%s:%s:%s">\n'
230 % (now, captcha_idx, Utils.sha_new(mm_cfg.SUBSCRIBE_FORM_SECRET +
233 mlist.internal_name() +
238 # Roster form substitutions
239 replacements['<mm-roster-form-start>'] = mlist.FormatFormStart('roster')
240 replacements['<mm-roster-option>'] = mlist.FormatRosterOptionForUser(lang)
241 # Options form substitutions
242 replacements['<mm-options-form-start>'] = mlist.FormatFormStart('options')
243 replacements['<mm-editing-options>'] = mlist.FormatEditingOption(lang)
244 replacements['<mm-info-button>'] = SubmitButton('UserOptions',
245 _('Edit Options')).Format()
246 # If only one language is enabled for this mailing list, omit the choice
248 if len(mlist.GetAvailableLanguages()) == 1:
251 displang = mlist.FormatButton('displang-button',
252 text = _("View this page in"))
253 replacements['<mm-displang-box>'] = displang
254 replacements['<mm-lang-form-start>'] = mlist.FormatFormStart('listinfo')
255 replacements['<mm-fullname-box>'] = mlist.FormatBox('fullname', size=30)
258 doc.AddItem(mlist.ParseTags('listinfo.html', replacements, lang))
264 if __name__ == "__main__":