Index: sitescripts/formmail/web/formmail2.py
===================================================================
new file mode 100644
--- /dev/null
+++ b/sitescripts/formmail/web/formmail2.py
@@ -0,0 +1,136 @@
+# This file is part of the Adblock Plus web scripts,
+# Copyright (C) 2006-2016 Eyeo GmbH
+#
+# Adblock Plus is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3 as
+# published by the Free Software Foundation.
+#
+# Adblock Plus 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 Adblock Plus.  If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import datetime
+import collections
+from urlparse import parse_qsl
+from sitescripts.utils import get_config, sendMail, setupStderr
+from sitescripts.web import registerUrlHandler
+
+
+class BadRequestError(Exception):
+    pass
+
+
+class ConfDict(collections.OrderedDict):
+    __slots__ = ('value',)
+
+
+def get_config_items():
+    config = get_config()
+    default_keys = set(config.defaults())
+    for name, value in config.items('formmail2'):
+        if name not in default_keys:
+            yield name, value
+
+
+def store_value(conf_dict, path, value):
+    head, tail = path[0], path[1:]
+    if head not in conf_dict:
+        conf_dict[head] = ConfDict()
+    if tail:
+        store_value(conf_dict[head], tail, value)
+    else:
+        conf_dict[head].value = value
+
+
+def conf_parse(conf_items):
+    conf_dict = ConfDict()
+    for key, value in conf_items:
+        path = key.split('.')
+        store_value(conf_dict, path, value)
+    return conf_dict
+
+
+def post_handler(handler):
+    def wrapped_handler(environ, start_response):
+        setupStderr(environ['wsgi.errors'])
+        response_headers = [('Content-Type', 'text/plain; charset=utf-8')]
+
+        try:
+            request_method = environ['REQUEST_METHOD'].upper()
+            url_encoded = 'application/x-www-form-urlencoded'
+            is_url_encoded = environ.get(
+                    'CONTENT_TYPE', '').startswith(url_encoded)
+            if request_method != 'POST' or not is_url_encoded:
+                raise BadRequestError('Unsupported request method')
+            try:
+                request_body_length = int(environ['CONTENT_LENGTH'])
+            except:
+                raise BadRequestError(
+                        'Invalid or missing Content-Length header')
+            request_body = environ['wsgi.input'].read(request_body_length)
+            params = {}
+            for key, value in parse_qsl(request_body):
+                params[key] = value.decode('utf-8').strip()
+
+            response = handler(params)
+        except BadRequestError as error:
+            start_response('400 Bad Request'+str(error), response_headers)
+            return str(error)
+        start_response('200 OK', response_headers)
+        return response
+    return wrapped_handler
+
+
+def make_handler(name, config):
+    try:
+        url = config['url'].value
+    except (KeyError, AttributeError):
+        raise Exception('No URL configured for form handler:' + name)
+    try:
+        template = config['template'].value
+    except (KeyError, AttributeError):
+        raise Exception('No template configured for form handler:' + name)
+    try:
+        fields = config['fields']
+        for field, spec in fields.items():
+            spec.value = set(spec.value.replace(' ', '').split(','))
+    except KeyError:
+        raise Exception('No fields configured for form handler:' + name)
+    if len(fields) == 0:
+        raise Exception('No fields configured for form handler:' + name)
+
+    @post_handler
+    def handler(params):
+        email_regex = r'^\w[\w.+!-]+@\w[\w.-]+\.[a-zA-Z]{2,6}$'
+        errors = []
+        for field, spec in fields.items():
+            if 'mandatory' in spec.value:
+                if field not in params.keys() or not params[field]:
+                    if 'mandatory' in spec:
+                        errors.append(spec['mandatory'].value)
+                    else:
+                        errors.append('No {} entered'.format(field))
+            if 'email' in spec.value and 'email' in params.keys():
+                if not re.search(email_regex, params['email']):
+                    if 'mandatory' in spec:
+                        errors.append(spec['mandatory'].value)
+                    else:
+                        errors.append('Invalid email address')
+        if errors:
+            raise BadRequestError('\n'.join(errors))
+
+        params['time'] = datetime.datetime.now()
+        sendMail(template, {'fields': params})
+        return ''
+    return url, handler
+
+
+conf_dict = conf_parse(get_config_items())
+for name, config in conf_dict.items():
+    url, handler = make_handler(name, config)
+    registerUrlHandler(url, handler)
