update mailman patch
authorRalf Jung <post@ralfj.de>
Sun, 3 Jan 2021 12:58:30 +0000 (13:58 +0100)
committerRalf Jung <post@ralfj.de>
Sun, 3 Jan 2021 12:58:30 +0000 (13:58 +0100)
roles/email/files/mailman-patched/Cgi/listinfo.py
roles/email/files/mailman-patched/Cgi/subscribe.py
roles/email/tasks/mailman.yml

index b46bab149c87d83f2b81bb7d2e713dd3b1d70398..0e38d03c71143b48e08b72a497b6e11a711d89fb 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2016 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2018 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
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -38,8 +38,7 @@ _ = i18n._
 i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
 
 
 i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
 
 
-
-
+\f
 def main():
     parts = Utils.GetPathPieces()
     if not parts:
 def main():
     parts = Utils.GetPathPieces()
     if not parts:
@@ -61,7 +60,7 @@ def main():
     # See if the user want to see this page in other language
     cgidata = cgi.FieldStorage()
     try:
     # See if the user want to see this page in other language
     cgidata = cgi.FieldStorage()
     try:
-        language = cgidata.getvalue('language')
+        language = cgidata.getfirst('language')
     except TypeError:
         # Someone crafted a POST with a bad Content-Type:.
         doc = Document()
     except TypeError:
         # Someone crafted a POST with a bad Content-Type:.
         doc = Document()
@@ -79,8 +78,7 @@ def main():
     list_listinfo(mlist, language)
 
 
     list_listinfo(mlist, language)
 
 
-
-
+\f
 def listinfo_overview(msg=''):
     # Present the general listinfo overview
     hostname = Utils.get_domain()
 def listinfo_overview(msg=''):
     # Present the general listinfo overview
     hostname = Utils.get_domain()
@@ -117,7 +115,7 @@ def listinfo_overview(msg=''):
             else:
                 advertised.append((mlist.GetScriptURL('listinfo'),
                                    mlist.real_name,
             else:
                 advertised.append((mlist.GetScriptURL('listinfo'),
                                    mlist.real_name,
-                                   Utils.websafe(mlist.description)))
+                                   Utils.websafe(mlist.GetDescription())))
     if msg:
         greeting = FontAttr(msg, color="ff5060", size="+1")
     else:
     if msg:
         greeting = FontAttr(msg, color="ff5060", size="+1")
     else:
@@ -175,8 +173,7 @@ def listinfo_overview(msg=''):
     print doc.Format()
 
 
     print doc.Format()
 
 
-
-
+\f
 def list_listinfo(mlist, lang):
     # Generate list specific listinfo
     doc = HeadlessDocument()
 def list_listinfo(mlist, lang):
     # Generate list specific listinfo
     doc = HeadlessDocument()
@@ -227,10 +224,10 @@ def list_listinfo(mlist, lang):
         # fill form
         replacements['<mm-subscribe-form-start>'] += (
                 '<input type="hidden" name="sub_form_token" value="%s:%s:%s">\n'
         # fill form
         replacements['<mm-subscribe-form-start>'] += (
                 '<input type="hidden" name="sub_form_token" value="%s:%s:%s">\n'
-                % (now, captcha_idx, Utils.sha_new(mm_cfg.SUBSCRIBE_FORM_SECRET +
-                          now +
-                          captcha_idx +
-                          mlist.internal_name() +
+                % (now, captcha_idx, Utils.sha_new(mm_cfg.SUBSCRIBE_FORM_SECRET + ":" +
+                          now + ":" +
+                          captcha_idx + ":" +
+                          mlist.internal_name() + ":" +
                           remote
                           ).hexdigest()
                     )
                           remote
                           ).hexdigest()
                     )
@@ -253,13 +250,25 @@ def list_listinfo(mlist, lang):
     replacements['<mm-displang-box>'] = displang
     replacements['<mm-lang-form-start>'] = mlist.FormatFormStart('listinfo')
     replacements['<mm-fullname-box>'] = mlist.FormatBox('fullname', size=30)
     replacements['<mm-displang-box>'] = displang
     replacements['<mm-lang-form-start>'] = mlist.FormatFormStart('listinfo')
     replacements['<mm-fullname-box>'] = mlist.FormatBox('fullname', size=30)
+    # If reCAPTCHA is enabled, display its user interface
+    if mm_cfg.RECAPTCHA_SITE_KEY:
+        noscript = _('This form requires JavaScript.')
+        replacements['<mm-recaptcha-ui>'] = (
+            """<tr><td>&nbsp;</td><td>
+            <noscript>%s</noscript>
+            <script src="https://www.google.com/recaptcha/api.js?hl=%s">
+            </script>
+            <div class="g-recaptcha" data-sitekey="%s"></div>
+            </td></tr>"""
+            % (noscript, lang, mm_cfg.RECAPTCHA_SITE_KEY))
+    else:
+        replacements['<mm-recaptcha-ui>'] = ''
 
     # Do the expansion.
     doc.AddItem(mlist.ParseTags('listinfo.html', replacements, lang))
     print doc.Format()
 
 
 
     # Do the expansion.
     doc.AddItem(mlist.ParseTags('listinfo.html', replacements, lang))
     print doc.Format()
 
 
-
-
+\f
 if __name__ == "__main__":
     main()
 if __name__ == "__main__":
     main()
index 153286dfb13865e38208dbf9232139cfde35dc3e..ea4ef9499014d17cdd6df4b6cb520b494617764b 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 1998-2016 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2018 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
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -22,6 +22,9 @@ import os
 import cgi
 import time
 import signal
 import cgi
 import time
 import signal
+import urllib
+import urllib2
+import json
 
 from Mailman import mm_cfg
 from Mailman import Utils
 
 from Mailman import mm_cfg
 from Mailman import Utils
@@ -36,14 +39,14 @@ from Mailman.Logging.Syslog import syslog
 
 SLASH = '/'
 ERRORSEP = '\n\n<p>'
 
 SLASH = '/'
 ERRORSEP = '\n\n<p>'
+COMMASPACE = ', '
 
 # Set up i18n
 _ = i18n._
 i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
 
 
 
 # Set up i18n
 _ = i18n._
 i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
 
 
-
-
+\f
 def main():
     doc = Document()
     doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
 def main():
     doc = Document()
     doc.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
@@ -73,7 +76,7 @@ def main():
     # for the results.  If not, use the list's preferred language.
     cgidata = cgi.FieldStorage()
     try:
     # for the results.  If not, use the list's preferred language.
     cgidata = cgi.FieldStorage()
     try:
-        language = cgidata.getvalue('language', '')
+        language = cgidata.getfirst('language', '')
     except TypeError:
         # Someone crafted a POST with a bad Content-Type:.
         doc.AddItem(Header(2, _("Error")))
     except TypeError:
         # Someone crafted a POST with a bad Content-Type:.
         doc.AddItem(Header(2, _("Error")))
@@ -114,19 +117,18 @@ def main():
         mlist.Unlock()
 
 
         mlist.Unlock()
 
 
-
-
+\f
 def process_form(mlist, doc, cgidata, lang):
     listowner = mlist.GetOwnerEmail()
     realname = mlist.real_name
     results = []
 
     # The email address being subscribed, required
 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', '').strip()
+    email = cgidata.getfirst('email', '').strip()
     if not email:
         results.append(_('You must supply a valid email address.'))
 
     if not email:
         results.append(_('You must supply a valid email address.'))
 
-    fullname = cgidata.getvalue('fullname', '')
+    fullname = cgidata.getfirst('fullname', '')
     # Canonicalize the full name
     fullname = Utils.canonstr(fullname, lang)
     # Who was doing the subscribing?
     # Canonicalize the full name
     fullname = Utils.canonstr(fullname, lang)
     # Who was doing the subscribing?
@@ -134,6 +136,26 @@ def process_form(mlist, doc, cgidata, lang):
              os.environ.get('HTTP_X_FORWARDED_FOR',
              os.environ.get('REMOTE_ADDR',
                             'unidentified origin')))
              os.environ.get('HTTP_X_FORWARDED_FOR',
              os.environ.get('REMOTE_ADDR',
                             'unidentified origin')))
+
+    # Check reCAPTCHA submission, if enabled
+    if mm_cfg.RECAPTCHA_SECRET_KEY:
+        request = urllib2.Request(
+            url = 'https://www.google.com/recaptcha/api/siteverify',
+            data = urllib.urlencode({
+                'secret': mm_cfg.RECAPTCHA_SECRET_KEY,
+                'response': cgidata.getvalue('g-recaptcha-response', ''),
+                'remoteip': remote}))
+        try:
+            httpresp = urllib2.urlopen(request)
+            captcha_response = json.load(httpresp)
+            httpresp.close()
+            if not captcha_response['success']:
+                e_codes = COMMASPACE.join(captcha_response['error-codes'])
+                results.append(_('reCAPTCHA validation failed: %(e_codes)s'))
+        except urllib2.URLError, e:
+            e_reason = e.reason
+            results.append(_('reCAPTCHA could not be validated: %(e_reason)s'))
+
     # Are we checking the hidden data?
     if mm_cfg.SUBSCRIBE_FORM_SECRET:
         now = int(time.time())
     # Are we checking the hidden data?
     if mm_cfg.SUBSCRIBE_FORM_SECRET:
         now = int(time.time())
@@ -147,15 +169,15 @@ def process_form(mlist, doc, cgidata, lang):
             #        for our hash so it doesn't matter.
             remote1 = remote.rsplit(':', 1)[0]
         try:
             #        for our hash so it doesn't matter.
             remote1 = remote.rsplit(':', 1)[0]
         try:
-            ftime, fcaptcha_idx, fhash = cgidata.getvalue('sub_form_token', '').split(':')
+            ftime, fcaptcha_idx, fhash = cgidata.getfirst('sub_form_token', '').split(':')
             then = int(ftime)
         except ValueError:
             ftime = fcaptcha_idx = fhash = ''
             then = 0
             then = int(ftime)
         except ValueError:
             ftime = fcaptcha_idx = fhash = ''
             then = 0
-        token = Utils.sha_new(mm_cfg.SUBSCRIBE_FORM_SECRET +
-                              ftime +
-                              fcaptcha_idx +
-                              mlist.internal_name() +
+        token = Utils.sha_new(mm_cfg.SUBSCRIBE_FORM_SECRET + ":" +
+                              ftime + ":" +
+                              fcaptcha_idx + ":" +
+                              mlist.internal_name() + ":" +
                               remote1).hexdigest()
         if ftime and now - then > mm_cfg.FORM_LIFETIME:
             results.append(_('The form is too old.  Please GET it again.'))
                               remote1).hexdigest()
         if ftime and now - then > mm_cfg.FORM_LIFETIME:
             results.append(_('The form is too old.  Please GET it again.'))
@@ -169,17 +191,17 @@ def process_form(mlist, doc, cgidata, lang):
             results.append(
     _('There was no hidden token in your submission or it was corrupted.'))
             results.append(_('You must GET the form before submitting it.'))
             results.append(
     _('There was no hidden token in your submission or it was corrupted.'))
             results.append(_('You must GET the form before submitting it.'))
-        # Check captcha
-        captcha_answer = cgidata.getvalue('captcha_answer', '')
-        if not Captcha.verify(fcaptcha_idx, captcha_answer, mm_cfg.CAPTCHAS):
-            results.append(_('This was not the right answer to the CAPTCHA question.'))
+    # Check captcha
+    captcha_answer = cgidata.getvalue('captcha_answer', '')
+    if not Captcha.verify(fcaptcha_idx, captcha_answer, mm_cfg.CAPTCHAS):
+        results.append(_('This was not the right answer to the CAPTCHA question.'))
     # Was an attempt made to subscribe the list to itself?
     if email == mlist.GetListEmail():
         syslog('mischief', '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
     # Was an attempt made to subscribe the list to itself?
     if email == mlist.GetListEmail():
         syslog('mischief', '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', '').strip()
-    confirmed = cgidata.getvalue('pw-conf', '').strip()
+    password = cgidata.getfirst('pw', '').strip()
+    confirmed = cgidata.getfirst('pw-conf', '').strip()
 
     if not password and not confirmed:
         password = Utils.MakeRandomPassword()
 
     if not password and not confirmed:
         password = Utils.MakeRandomPassword()
@@ -189,11 +211,11 @@ def process_form(mlist, doc, cgidata, lang):
         results.append(_('Your passwords did not match.'))
 
     # Get the digest option for the subscription.
         results.append(_('Your passwords did not match.'))
 
     # Get the digest option for the subscription.
-    digestflag = cgidata.getvalue('digest')
+    digestflag = cgidata.getfirst('digest')
     if digestflag:
         try:
             digest = int(digestflag)
     if digestflag:
         try:
             digest = int(digestflag)
-        except ValueError:
+        except (TypeError, ValueError):
             digest = 0
     else:
         digest = mlist.digest_is_default
             digest = 0
     else:
         digest = mlist.digest_is_default
@@ -318,8 +340,7 @@ You have been successfully subscribed to the %(realname)s mailing list.""")
     print_results(mlist, results, doc, lang)
 
 
     print_results(mlist, results, doc, lang)
 
 
-
-
+\f
 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
 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
index 0988edb2e7137cbe16b572f054714e40115b66aa..cba451906de89d1ef3d7453012a99c960aec2022 100644 (file)
@@ -21,8 +21,8 @@
 - name: check if all the files have the right checksums to be patched
   shell: 'echo "{{item}}" | sha256sum -c'
   loop:
 - name: check if all the files have the right checksums to be patched
   shell: 'echo "{{item}}" | sha256sum -c'
   loop:
-    - "26b4cbb7c5bde8badf741e31975235e74abb932037d77d862cf00b412726c2c2  /usr/lib/mailman/Mailman/Cgi/listinfo.py"
-    - "cbef3d8cb6b65e4c9b2462f8627966d55dd52caa2e626c87241c4f8d47477dc7  /usr/lib/mailman/Mailman/Cgi/subscribe.py"
+    - "621368ef38d991be46e4537b1d5444276579cd60cc721a749d500dd0b98efe27  /usr/lib/mailman/Mailman/Cgi/listinfo.py"
+    - "c6e46afe1c016d6853c8397916a6f6fd88c6cea71ae890ef3680617f1f8f7c9a  /usr/lib/mailman/Mailman/Cgi/subscribe.py"
   changed_when: False
   when: mailman_patched.rc != 0
 - name: install patched python files
   changed_when: False
   when: mailman_patched.rc != 0
 - name: install patched python files