We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Validation of redirect address in UI 
There are several CMSs that require the forwarding address to be specified partially in uppercase. Validation does not allow this.

![изображение](https://github.com/Mailu/Mailu/assets/8946023/0ea396d9-dc7b-41d9-b596-2dc71847842f)

![изображение](https://github.com/Mailu/Mailu/assets/8946023/01e43804-5486-4e72-983a-ab4a85e2c4bb)

```
grep MAILU_VERSION docker-compose.yml mailu.env
docker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}nginx:${MAILU_VERSION:-2.0}
docker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}unbound:${MAILU_VERSION:-2.0}
docker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}admin:${MAILU_VERSION:-2.0}
docker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}dovecot:${MAILU_VERSION:-2.0}
docker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}postfix:${MAILU_VERSION:-2.0}
docker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}oletools:${MAILU_VERSION:-2.0}
docker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}rspamd:${MAILU_VERSION:-2.0}
docker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}radicale:${MAILU_VERSION:-2.0}
docker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}fetchmail:${MAILU_VERSION:-2.0}
docker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}webmail:${MAILU_VERSION:-2.0}
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `core/admin/mailu/ui/forms.py`
Content:
```
1 from wtforms import validators, fields, widgets
2 from wtforms_components import fields as fields_
3 from flask_babel import lazy_gettext as _
4 
5 import flask_login
6 import flask_wtf
7 import re
8 import ipaddress
9 
10 LOCALPART_REGEX = "^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*$"
11 
12 class DestinationField(fields.SelectMultipleField):
13     """ Allow for multiple emails selection from current user choices and
14     additional email addresses.
15     """
16 
17     validator = re.compile(r'^.+@([^.@][^@]+)$', re.IGNORECASE)
18 
19     def iter_choices(self):
20         managed = [
21             str(email)
22             for email in flask_login.current_user.get_managed_emails()
23         ]
24         for email in managed:
25             selected = self.data is not None and self.coerce(email) in self.data
26             yield (email, email, selected)
27         for email in self.data or ():
28             if email not in managed:
29                 yield (email, email, True)
30 
31     def pre_validate(self, form):
32         for item in self.data:
33             if not self.validator.match(item):
34                 raise validators.ValidationError(_('Invalid email address.'))
35 
36 class MultipleEmailAddressesVerify(object):
37     def __init__(self,message=_('Invalid email address.')):
38         self.message = message
39 
40     def __call__(self, form, field):
41         pattern = re.compile(r'^([_a-z0-9\-\+]+)(\.[_a-z0-9\-\+]+)*@([a-z0-9\-]{1,}\.)*([a-z]{1,})(,([_a-z0-9\-\+]+)(\.[_a-z0-9\-\+]+)*@([a-z0-9\-]{1,}\.)*([a-z]{2,}))*$')
42         if not pattern.match(field.data.replace(" ", "")):
43             raise validators.ValidationError(self.message)
44 
45 class MultipleFoldersVerify(object):
46     """ Ensure that we have CSV formated data """
47     def __init__(self,message=_('Invalid list of folders.')):
48         self.message = message
49 
50     def __call__(self, form, field):
51         pattern = re.compile(r'^[^,]+(,[^,]+)*$')
52         if not pattern.match(field.data.replace(" ", "")):
53             raise validators.ValidationError(self.message)
54 
55 class ConfirmationForm(flask_wtf.FlaskForm):
56     submit = fields.SubmitField(_('Confirm'))
57 
58 class DomainForm(flask_wtf.FlaskForm):
59     name = fields.StringField(_('Domain name'), [validators.DataRequired()])
60     max_users = fields_.IntegerField(_('Maximum user count'), [validators.NumberRange(min=-1)], default=10)
61     max_aliases = fields_.IntegerField(_('Maximum alias count'), [validators.NumberRange(min=-1)], default=10)
62     max_quota_bytes = fields_.IntegerSliderField(_('Maximum user quota'), default=0)
63     signup_enabled = fields.BooleanField(_('Enable sign-up'), default=False)
64     comment = fields.StringField(_('Comment'))
65     submit = fields.SubmitField(_('Save'))
66 
67 
68 class DomainSignupForm(flask_wtf.FlaskForm):
69     name = fields.StringField(_('Domain name'), [validators.DataRequired()])
70     localpart = fields.StringField(_('Initial admin'), [validators.DataRequired()])
71     pw = fields.PasswordField(_('Admin password'), [validators.DataRequired()])
72     pw2 = fields.PasswordField(_('Confirm password'), [validators.EqualTo('pw')])
73     pwned = fields.HiddenField(label='', default=-1)
74     captcha = flask_wtf.RecaptchaField()
75     submit = fields.SubmitField(_('Create'))
76 
77 
78 class AlternativeForm(flask_wtf.FlaskForm):
79     name = fields.StringField(_('Alternative name'), [validators.DataRequired()])
80     submit = fields.SubmitField(_('Save'))
81 
82 
83 class RelayForm(flask_wtf.FlaskForm):
84     name = fields.StringField(_('Relayed domain name'), [validators.DataRequired()])
85     smtp = fields.StringField(_('Remote host'))
86     comment = fields.StringField(_('Comment'))
87     submit = fields.SubmitField(_('Save'))
88 
89 
90 class UserForm(flask_wtf.FlaskForm):
91     localpart = fields.StringField(_('E-mail'), [validators.DataRequired(), validators.Regexp(LOCALPART_REGEX)])
92     pw = fields.PasswordField(_('Password'))
93     pw2 = fields.PasswordField(_('Confirm password'), [validators.EqualTo('pw')])
94     pwned = fields.HiddenField(label='', default=-1)
95     quota_bytes = fields_.IntegerSliderField(_('Quota'), default=10**9)
96     enable_imap = fields.BooleanField(_('Allow IMAP access'), default=True)
97     enable_pop = fields.BooleanField(_('Allow POP3 access'), default=True)
98     allow_spoofing = fields.BooleanField(_('Allow the user to spoof the sender (send email as anyone)'), default=False)
99     displayed_name = fields.StringField(_('Displayed name'))
100     comment = fields.StringField(_('Comment'))
101     enabled = fields.BooleanField(_('Enabled'), default=True)
102     change_pw_next_login = fields.BooleanField(_('Force password change at next login'), default=True)
103     submit = fields.SubmitField(_('Save'))
104 
105 
106 class UserSignupForm(flask_wtf.FlaskForm):
107     localpart = fields.StringField(_('Email address'), [validators.DataRequired(), validators.Regexp(LOCALPART_REGEX)])
108     pw = fields.PasswordField(_('Password'), [validators.DataRequired()])
109     pw2 = fields.PasswordField(_('Confirm password'), [validators.EqualTo('pw')])
110     pwned = fields.HiddenField(label='', default=-1)
111     submit = fields.SubmitField(_('Sign up'))
112 
113 class UserSignupFormCaptcha(UserSignupForm):
114     captcha = flask_wtf.RecaptchaField()
115 
116 class UserSettingsForm(flask_wtf.FlaskForm):
117     displayed_name = fields.StringField(_('Displayed name'))
118     spam_enabled = fields.BooleanField(_('Enable spam filter'))
119     spam_mark_as_read = fields.BooleanField(_('Enable marking spam mails as read'))
120     spam_threshold = fields_.IntegerSliderField(_('Spam filter tolerance'))
121     forward_enabled = fields.BooleanField(_('Enable forwarding'))
122     forward_keep = fields.BooleanField(_('Keep a copy of the emails'))
123     forward_destination = fields.StringField(_('Destination'), [validators.Optional(), MultipleEmailAddressesVerify()])
124     submit = fields.SubmitField(_('Save settings'))
125 
126 
127 class UserPasswordForm(flask_wtf.FlaskForm):
128     pw = fields.PasswordField(_('Password'), [validators.DataRequired()])
129     pw2 = fields.PasswordField(_('Password check'), [validators.DataRequired()])
130     pwned = fields.HiddenField(label='', default=-1)
131     submit = fields.SubmitField(_('Update password'))
132 
133 class UserPasswordChangeForm(flask_wtf.FlaskForm):
134     current_pw = fields.PasswordField(_('Current password'), [validators.DataRequired()])
135     pw = fields.PasswordField(_('Password'), [validators.DataRequired()])
136     pw2 = fields.PasswordField(_('Password check'), [validators.DataRequired()])
137     pwned = fields.HiddenField(label='', default=-1)
138     submit = fields.SubmitField(_('Update password'))
139 
140 class UserReplyForm(flask_wtf.FlaskForm):
141     reply_enabled = fields.BooleanField(_('Enable automatic reply'))
142     reply_subject = fields.StringField(_('Reply subject'))
143     reply_body = fields.StringField(_('Reply body'),
144         widget=widgets.TextArea())
145     reply_startdate = fields.DateField(_('Start of vacation'))
146     reply_enddate = fields.DateField(_('End of vacation'))
147     submit = fields.SubmitField(_('Update'))
148 
149 
150 class TokenForm(flask_wtf.FlaskForm):
151     displayed_password = fields.StringField(
152         _('Your token (write it down, as it will never be displayed again)')
153     )
154     raw_password = fields.HiddenField([validators.DataRequired()])
155     comment = fields.StringField(_('Comment'))
156     ip = fields.StringField(
157         _('Authorized IP'), [validators.Optional()]
158     )
159     submit = fields.SubmitField(_('Save'))
160 
161     def validate_ip(form, field):
162         if not field.data:
163             return True
164         try:
165             for candidate in field.data.replace(' ','').split(','):
166                 ipaddress.ip_network(candidate, False)
167         except:
168             raise validators.ValidationError('Not a valid list of CIDRs')
169 
170 class AliasForm(flask_wtf.FlaskForm):
171     localpart = fields.StringField(_('Alias'), [validators.DataRequired(), validators.Regexp(LOCALPART_REGEX)])
172     wildcard = fields.BooleanField(
173         _('Use SQL LIKE Syntax (e.g. for catch-all aliases)'))
174     destination = DestinationField(_('Destination'))
175     comment = fields.StringField(_('Comment'))
176     submit = fields.SubmitField(_('Save'))
177 
178 
179 class AdminForm(flask_wtf.FlaskForm):
180     admin = fields.SelectField(_('Admin email'), choices=[])
181     submit = fields.SubmitField(_('Submit'))
182 
183 
184 class ManagerForm(flask_wtf.FlaskForm):
185     manager = fields.SelectField(_('Manager email'))
186     submit = fields.SubmitField(_('Submit'))
187 
188 
189 class FetchForm(flask_wtf.FlaskForm):
190     protocol = fields.SelectField(_('Protocol'), choices=[
191         ('imap', 'IMAP'), ('pop3', 'POP3')
192     ])
193     host = fields.StringField(_('Hostname or IP'), [validators.DataRequired()])
194     port = fields.IntegerField(_('TCP port'), [validators.DataRequired(), validators.NumberRange(min=0, max=65535)], default=993)
195     tls = fields.BooleanField(_('Enable TLS'), default=True)
196     username = fields.StringField(_('Username'), [validators.DataRequired()])
197     password = fields.PasswordField(_('Password'))
198     keep = fields.BooleanField(_('Keep emails on the server'))
199     scan = fields.BooleanField(_('Rescan emails locally'))
200     folders = fields.StringField(_('Folders to fetch on the server'), [validators.Optional(), MultipleFoldersVerify()], default='INBOX,Junk')
201     submit = fields.SubmitField(_('Submit'))
202 
203 
204 class AnnouncementForm(flask_wtf.FlaskForm):
205     announcement_subject = fields.StringField(_('Announcement subject'),
206         [validators.DataRequired()])
207     announcement_body = fields.StringField(_('Announcement body'),
208         [validators.DataRequired()], widget=widgets.TextArea())
209     submit = fields.SubmitField(_('Send'))
210
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/core/admin/mailu/ui/forms.py b/core/admin/mailu/ui/forms.py
--- a/core/admin/mailu/ui/forms.py
+++ b/core/admin/mailu/ui/forms.py
@@ -38,7 +38,7 @@
         self.message = message
 
     def __call__(self, form, field):
-        pattern = re.compile(r'^([_a-z0-9\-\+]+)(\.[_a-z0-9\-\+]+)*@([a-z0-9\-]{1,}\.)*([a-z]{1,})(,([_a-z0-9\-\+]+)(\.[_a-z0-9\-\+]+)*@([a-z0-9\-]{1,}\.)*([a-z]{2,}))*$')
+        pattern = re.compile(r'^([_a-z0-9\-\+]+)(\.[_a-z0-9\-\+]+)*@([a-z0-9\-]{1,}\.)*([a-z]{1,})(,([_a-z0-9\-\+]+)(\.[_a-z0-9\-\+]+)*@([a-z0-9\-]{1,}\.)*([a-z]{2,}))*$', re.IGNORECASE)
         if not pattern.match(field.data.replace(" ", "")):
             raise validators.ValidationError(self.message)

{"golden_diff": "diff --git a/core/admin/mailu/ui/forms.py b/core/admin/mailu/ui/forms.py\n--- a/core/admin/mailu/ui/forms.py\n+++ b/core/admin/mailu/ui/forms.py\n@@ -38,7 +38,7 @@\n         self.message = message\n \n     def __call__(self, form, field):\n-        pattern = re.compile(r'^([_a-z0-9\\-\\+]+)(\\.[_a-z0-9\\-\\+]+)*@([a-z0-9\\-]{1,}\\.)*([a-z]{1,})(,([_a-z0-9\\-\\+]+)(\\.[_a-z0-9\\-\\+]+)*@([a-z0-9\\-]{1,}\\.)*([a-z]{2,}))*$')\n+        pattern = re.compile(r'^([_a-z0-9\\-\\+]+)(\\.[_a-z0-9\\-\\+]+)*@([a-z0-9\\-]{1,}\\.)*([a-z]{1,})(,([_a-z0-9\\-\\+]+)(\\.[_a-z0-9\\-\\+]+)*@([a-z0-9\\-]{1,}\\.)*([a-z]{2,}))*$', re.IGNORECASE)\n         if not pattern.match(field.data.replace(\" \", \"\")):\n             raise validators.ValidationError(self.message)\n", "issue": "Validation of redirect address in UI \nThere are several CMSs that require the forwarding address to be specified partially in uppercase. Validation does not allow this.\r\n\r\n![\u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435](https://github.com/Mailu/Mailu/assets/8946023/0ea396d9-dc7b-41d9-b596-2dc71847842f)\r\n\r\n![\u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435](https://github.com/Mailu/Mailu/assets/8946023/01e43804-5486-4e72-983a-ab4a85e2c4bb)\r\n\r\n```\r\ngrep MAILU_VERSION docker-compose.yml mailu.env\r\ndocker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}nginx:${MAILU_VERSION:-2.0}\r\ndocker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}unbound:${MAILU_VERSION:-2.0}\r\ndocker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}admin:${MAILU_VERSION:-2.0}\r\ndocker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}dovecot:${MAILU_VERSION:-2.0}\r\ndocker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}postfix:${MAILU_VERSION:-2.0}\r\ndocker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}oletools:${MAILU_VERSION:-2.0}\r\ndocker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}rspamd:${MAILU_VERSION:-2.0}\r\ndocker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}radicale:${MAILU_VERSION:-2.0}\r\ndocker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}fetchmail:${MAILU_VERSION:-2.0}\r\ndocker-compose.yml:    image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}webmail:${MAILU_VERSION:-2.0}\r\n```\r\n\n", "before_files": [{"content": "from wtforms import validators, fields, widgets\nfrom wtforms_components import fields as fields_\nfrom flask_babel import lazy_gettext as _\n\nimport flask_login\nimport flask_wtf\nimport re\nimport ipaddress\n\nLOCALPART_REGEX = \"^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*$\"\n\nclass DestinationField(fields.SelectMultipleField):\n    \"\"\" Allow for multiple emails selection from current user choices and\n    additional email addresses.\n    \"\"\"\n\n    validator = re.compile(r'^.+@([^.@][^@]+)$', re.IGNORECASE)\n\n    def iter_choices(self):\n        managed = [\n            str(email)\n            for email in flask_login.current_user.get_managed_emails()\n        ]\n        for email in managed:\n            selected = self.data is not None and self.coerce(email) in self.data\n            yield (email, email, selected)\n        for email in self.data or ():\n            if email not in managed:\n                yield (email, email, True)\n\n    def pre_validate(self, form):\n        for item in self.data:\n            if not self.validator.match(item):\n                raise validators.ValidationError(_('Invalid email address.'))\n\nclass MultipleEmailAddressesVerify(object):\n    def __init__(self,message=_('Invalid email address.')):\n        self.message = message\n\n    def __call__(self, form, field):\n        pattern = re.compile(r'^([_a-z0-9\\-\\+]+)(\\.[_a-z0-9\\-\\+]+)*@([a-z0-9\\-]{1,}\\.)*([a-z]{1,})(,([_a-z0-9\\-\\+]+)(\\.[_a-z0-9\\-\\+]+)*@([a-z0-9\\-]{1,}\\.)*([a-z]{2,}))*$')\n        if not pattern.match(field.data.replace(\" \", \"\")):\n            raise validators.ValidationError(self.message)\n\nclass MultipleFoldersVerify(object):\n    \"\"\" Ensure that we have CSV formated data \"\"\"\n    def __init__(self,message=_('Invalid list of folders.')):\n        self.message = message\n\n    def __call__(self, form, field):\n        pattern = re.compile(r'^[^,]+(,[^,]+)*$')\n        if not pattern.match(field.data.replace(\" \", \"\")):\n            raise validators.ValidationError(self.message)\n\nclass ConfirmationForm(flask_wtf.FlaskForm):\n    submit = fields.SubmitField(_('Confirm'))\n\nclass DomainForm(flask_wtf.FlaskForm):\n    name = fields.StringField(_('Domain name'), [validators.DataRequired()])\n    max_users = fields_.IntegerField(_('Maximum user count'), [validators.NumberRange(min=-1)], default=10)\n    max_aliases = fields_.IntegerField(_('Maximum alias count'), [validators.NumberRange(min=-1)], default=10)\n    max_quota_bytes = fields_.IntegerSliderField(_('Maximum user quota'), default=0)\n    signup_enabled = fields.BooleanField(_('Enable sign-up'), default=False)\n    comment = fields.StringField(_('Comment'))\n    submit = fields.SubmitField(_('Save'))\n\n\nclass DomainSignupForm(flask_wtf.FlaskForm):\n    name = fields.StringField(_('Domain name'), [validators.DataRequired()])\n    localpart = fields.StringField(_('Initial admin'), [validators.DataRequired()])\n    pw = fields.PasswordField(_('Admin password'), [validators.DataRequired()])\n    pw2 = fields.PasswordField(_('Confirm password'), [validators.EqualTo('pw')])\n    pwned = fields.HiddenField(label='', default=-1)\n    captcha = flask_wtf.RecaptchaField()\n    submit = fields.SubmitField(_('Create'))\n\n\nclass AlternativeForm(flask_wtf.FlaskForm):\n    name = fields.StringField(_('Alternative name'), [validators.DataRequired()])\n    submit = fields.SubmitField(_('Save'))\n\n\nclass RelayForm(flask_wtf.FlaskForm):\n    name = fields.StringField(_('Relayed domain name'), [validators.DataRequired()])\n    smtp = fields.StringField(_('Remote host'))\n    comment = fields.StringField(_('Comment'))\n    submit = fields.SubmitField(_('Save'))\n\n\nclass UserForm(flask_wtf.FlaskForm):\n    localpart = fields.StringField(_('E-mail'), [validators.DataRequired(), validators.Regexp(LOCALPART_REGEX)])\n    pw = fields.PasswordField(_('Password'))\n    pw2 = fields.PasswordField(_('Confirm password'), [validators.EqualTo('pw')])\n    pwned = fields.HiddenField(label='', default=-1)\n    quota_bytes = fields_.IntegerSliderField(_('Quota'), default=10**9)\n    enable_imap = fields.BooleanField(_('Allow IMAP access'), default=True)\n    enable_pop = fields.BooleanField(_('Allow POP3 access'), default=True)\n    allow_spoofing = fields.BooleanField(_('Allow the user to spoof the sender (send email as anyone)'), default=False)\n    displayed_name = fields.StringField(_('Displayed name'))\n    comment = fields.StringField(_('Comment'))\n    enabled = fields.BooleanField(_('Enabled'), default=True)\n    change_pw_next_login = fields.BooleanField(_('Force password change at next login'), default=True)\n    submit = fields.SubmitField(_('Save'))\n\n\nclass UserSignupForm(flask_wtf.FlaskForm):\n    localpart = fields.StringField(_('Email address'), [validators.DataRequired(), validators.Regexp(LOCALPART_REGEX)])\n    pw = fields.PasswordField(_('Password'), [validators.DataRequired()])\n    pw2 = fields.PasswordField(_('Confirm password'), [validators.EqualTo('pw')])\n    pwned = fields.HiddenField(label='', default=-1)\n    submit = fields.SubmitField(_('Sign up'))\n\nclass UserSignupFormCaptcha(UserSignupForm):\n    captcha = flask_wtf.RecaptchaField()\n\nclass UserSettingsForm(flask_wtf.FlaskForm):\n    displayed_name = fields.StringField(_('Displayed name'))\n    spam_enabled = fields.BooleanField(_('Enable spam filter'))\n    spam_mark_as_read = fields.BooleanField(_('Enable marking spam mails as read'))\n    spam_threshold = fields_.IntegerSliderField(_('Spam filter tolerance'))\n    forward_enabled = fields.BooleanField(_('Enable forwarding'))\n    forward_keep = fields.BooleanField(_('Keep a copy of the emails'))\n    forward_destination = fields.StringField(_('Destination'), [validators.Optional(), MultipleEmailAddressesVerify()])\n    submit = fields.SubmitField(_('Save settings'))\n\n\nclass UserPasswordForm(flask_wtf.FlaskForm):\n    pw = fields.PasswordField(_('Password'), [validators.DataRequired()])\n    pw2 = fields.PasswordField(_('Password check'), [validators.DataRequired()])\n    pwned = fields.HiddenField(label='', default=-1)\n    submit = fields.SubmitField(_('Update password'))\n\nclass UserPasswordChangeForm(flask_wtf.FlaskForm):\n    current_pw = fields.PasswordField(_('Current password'), [validators.DataRequired()])\n    pw = fields.PasswordField(_('Password'), [validators.DataRequired()])\n    pw2 = fields.PasswordField(_('Password check'), [validators.DataRequired()])\n    pwned = fields.HiddenField(label='', default=-1)\n    submit = fields.SubmitField(_('Update password'))\n\nclass UserReplyForm(flask_wtf.FlaskForm):\n    reply_enabled = fields.BooleanField(_('Enable automatic reply'))\n    reply_subject = fields.StringField(_('Reply subject'))\n    reply_body = fields.StringField(_('Reply body'),\n        widget=widgets.TextArea())\n    reply_startdate = fields.DateField(_('Start of vacation'))\n    reply_enddate = fields.DateField(_('End of vacation'))\n    submit = fields.SubmitField(_('Update'))\n\n\nclass TokenForm(flask_wtf.FlaskForm):\n    displayed_password = fields.StringField(\n        _('Your token (write it down, as it will never be displayed again)')\n    )\n    raw_password = fields.HiddenField([validators.DataRequired()])\n    comment = fields.StringField(_('Comment'))\n    ip = fields.StringField(\n        _('Authorized IP'), [validators.Optional()]\n    )\n    submit = fields.SubmitField(_('Save'))\n\n    def validate_ip(form, field):\n        if not field.data:\n            return True\n        try:\n            for candidate in field.data.replace(' ','').split(','):\n                ipaddress.ip_network(candidate, False)\n        except:\n            raise validators.ValidationError('Not a valid list of CIDRs')\n\nclass AliasForm(flask_wtf.FlaskForm):\n    localpart = fields.StringField(_('Alias'), [validators.DataRequired(), validators.Regexp(LOCALPART_REGEX)])\n    wildcard = fields.BooleanField(\n        _('Use SQL LIKE Syntax (e.g. for catch-all aliases)'))\n    destination = DestinationField(_('Destination'))\n    comment = fields.StringField(_('Comment'))\n    submit = fields.SubmitField(_('Save'))\n\n\nclass AdminForm(flask_wtf.FlaskForm):\n    admin = fields.SelectField(_('Admin email'), choices=[])\n    submit = fields.SubmitField(_('Submit'))\n\n\nclass ManagerForm(flask_wtf.FlaskForm):\n    manager = fields.SelectField(_('Manager email'))\n    submit = fields.SubmitField(_('Submit'))\n\n\nclass FetchForm(flask_wtf.FlaskForm):\n    protocol = fields.SelectField(_('Protocol'), choices=[\n        ('imap', 'IMAP'), ('pop3', 'POP3')\n    ])\n    host = fields.StringField(_('Hostname or IP'), [validators.DataRequired()])\n    port = fields.IntegerField(_('TCP port'), [validators.DataRequired(), validators.NumberRange(min=0, max=65535)], default=993)\n    tls = fields.BooleanField(_('Enable TLS'), default=True)\n    username = fields.StringField(_('Username'), [validators.DataRequired()])\n    password = fields.PasswordField(_('Password'))\n    keep = fields.BooleanField(_('Keep emails on the server'))\n    scan = fields.BooleanField(_('Rescan emails locally'))\n    folders = fields.StringField(_('Folders to fetch on the server'), [validators.Optional(), MultipleFoldersVerify()], default='INBOX,Junk')\n    submit = fields.SubmitField(_('Submit'))\n\n\nclass AnnouncementForm(flask_wtf.FlaskForm):\n    announcement_subject = fields.StringField(_('Announcement subject'),\n        [validators.DataRequired()])\n    announcement_body = fields.StringField(_('Announcement body'),\n        [validators.DataRequired()], widget=widgets.TextArea())\n    submit = fields.SubmitField(_('Send'))\n", "path": "core/admin/mailu/ui/forms.py"}], "after_files": [{"content": "from wtforms import validators, fields, widgets\nfrom wtforms_components import fields as fields_\nfrom flask_babel import lazy_gettext as _\n\nimport flask_login\nimport flask_wtf\nimport re\nimport ipaddress\n\nLOCALPART_REGEX = \"^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*$\"\n\nclass DestinationField(fields.SelectMultipleField):\n    \"\"\" Allow for multiple emails selection from current user choices and\n    additional email addresses.\n    \"\"\"\n\n    validator = re.compile(r'^.+@([^.@][^@]+)$', re.IGNORECASE)\n\n    def iter_choices(self):\n        managed = [\n            str(email)\n            for email in flask_login.current_user.get_managed_emails()\n        ]\n        for email in managed:\n            selected = self.data is not None and self.coerce(email) in self.data\n            yield (email, email, selected)\n        for email in self.data or ():\n            if email not in managed:\n                yield (email, email, True)\n\n    def pre_validate(self, form):\n        for item in self.data:\n            if not self.validator.match(item):\n                raise validators.ValidationError(_('Invalid email address.'))\n\nclass MultipleEmailAddressesVerify(object):\n    def __init__(self,message=_('Invalid email address.')):\n        self.message = message\n\n    def __call__(self, form, field):\n        pattern = re.compile(r'^([_a-z0-9\\-\\+]+)(\\.[_a-z0-9\\-\\+]+)*@([a-z0-9\\-]{1,}\\.)*([a-z]{1,})(,([_a-z0-9\\-\\+]+)(\\.[_a-z0-9\\-\\+]+)*@([a-z0-9\\-]{1,}\\.)*([a-z]{2,}))*$', re.IGNORECASE)\n        if not pattern.match(field.data.replace(\" \", \"\")):\n            raise validators.ValidationError(self.message)\n\nclass MultipleFoldersVerify(object):\n    \"\"\" Ensure that we have CSV formated data \"\"\"\n    def __init__(self,message=_('Invalid list of folders.')):\n        self.message = message\n\n    def __call__(self, form, field):\n        pattern = re.compile(r'^[^,]+(,[^,]+)*$')\n        if not pattern.match(field.data.replace(\" \", \"\")):\n            raise validators.ValidationError(self.message)\n\nclass ConfirmationForm(flask_wtf.FlaskForm):\n    submit = fields.SubmitField(_('Confirm'))\n\nclass DomainForm(flask_wtf.FlaskForm):\n    name = fields.StringField(_('Domain name'), [validators.DataRequired()])\n    max_users = fields_.IntegerField(_('Maximum user count'), [validators.NumberRange(min=-1)], default=10)\n    max_aliases = fields_.IntegerField(_('Maximum alias count'), [validators.NumberRange(min=-1)], default=10)\n    max_quota_bytes = fields_.IntegerSliderField(_('Maximum user quota'), default=0)\n    signup_enabled = fields.BooleanField(_('Enable sign-up'), default=False)\n    comment = fields.StringField(_('Comment'))\n    submit = fields.SubmitField(_('Save'))\n\n\nclass DomainSignupForm(flask_wtf.FlaskForm):\n    name = fields.StringField(_('Domain name'), [validators.DataRequired()])\n    localpart = fields.StringField(_('Initial admin'), [validators.DataRequired()])\n    pw = fields.PasswordField(_('Admin password'), [validators.DataRequired()])\n    pw2 = fields.PasswordField(_('Confirm password'), [validators.EqualTo('pw')])\n    pwned = fields.HiddenField(label='', default=-1)\n    captcha = flask_wtf.RecaptchaField()\n    submit = fields.SubmitField(_('Create'))\n\n\nclass AlternativeForm(flask_wtf.FlaskForm):\n    name = fields.StringField(_('Alternative name'), [validators.DataRequired()])\n    submit = fields.SubmitField(_('Save'))\n\n\nclass RelayForm(flask_wtf.FlaskForm):\n    name = fields.StringField(_('Relayed domain name'), [validators.DataRequired()])\n    smtp = fields.StringField(_('Remote host'))\n    comment = fields.StringField(_('Comment'))\n    submit = fields.SubmitField(_('Save'))\n\n\nclass UserForm(flask_wtf.FlaskForm):\n    localpart = fields.StringField(_('E-mail'), [validators.DataRequired(), validators.Regexp(LOCALPART_REGEX)])\n    pw = fields.PasswordField(_('Password'))\n    pw2 = fields.PasswordField(_('Confirm password'), [validators.EqualTo('pw')])\n    pwned = fields.HiddenField(label='', default=-1)\n    quota_bytes = fields_.IntegerSliderField(_('Quota'), default=10**9)\n    enable_imap = fields.BooleanField(_('Allow IMAP access'), default=True)\n    enable_pop = fields.BooleanField(_('Allow POP3 access'), default=True)\n    allow_spoofing = fields.BooleanField(_('Allow the user to spoof the sender (send email as anyone)'), default=False)\n    displayed_name = fields.StringField(_('Displayed name'))\n    comment = fields.StringField(_('Comment'))\n    enabled = fields.BooleanField(_('Enabled'), default=True)\n    change_pw_next_login = fields.BooleanField(_('Force password change at next login'), default=True)\n    submit = fields.SubmitField(_('Save'))\n\n\nclass UserSignupForm(flask_wtf.FlaskForm):\n    localpart = fields.StringField(_('Email address'), [validators.DataRequired(), validators.Regexp(LOCALPART_REGEX)])\n    pw = fields.PasswordField(_('Password'), [validators.DataRequired()])\n    pw2 = fields.PasswordField(_('Confirm password'), [validators.EqualTo('pw')])\n    pwned = fields.HiddenField(label='', default=-1)\n    submit = fields.SubmitField(_('Sign up'))\n\nclass UserSignupFormCaptcha(UserSignupForm):\n    captcha = flask_wtf.RecaptchaField()\n\nclass UserSettingsForm(flask_wtf.FlaskForm):\n    displayed_name = fields.StringField(_('Displayed name'))\n    spam_enabled = fields.BooleanField(_('Enable spam filter'))\n    spam_mark_as_read = fields.BooleanField(_('Enable marking spam mails as read'))\n    spam_threshold = fields_.IntegerSliderField(_('Spam filter tolerance'))\n    forward_enabled = fields.BooleanField(_('Enable forwarding'))\n    forward_keep = fields.BooleanField(_('Keep a copy of the emails'))\n    forward_destination = fields.StringField(_('Destination'), [validators.Optional(), MultipleEmailAddressesVerify()])\n    submit = fields.SubmitField(_('Save settings'))\n\n\nclass UserPasswordForm(flask_wtf.FlaskForm):\n    pw = fields.PasswordField(_('Password'), [validators.DataRequired()])\n    pw2 = fields.PasswordField(_('Password check'), [validators.DataRequired()])\n    pwned = fields.HiddenField(label='', default=-1)\n    submit = fields.SubmitField(_('Update password'))\n\nclass UserPasswordChangeForm(flask_wtf.FlaskForm):\n    current_pw = fields.PasswordField(_('Current password'), [validators.DataRequired()])\n    pw = fields.PasswordField(_('Password'), [validators.DataRequired()])\n    pw2 = fields.PasswordField(_('Password check'), [validators.DataRequired()])\n    pwned = fields.HiddenField(label='', default=-1)\n    submit = fields.SubmitField(_('Update password'))\n\nclass UserReplyForm(flask_wtf.FlaskForm):\n    reply_enabled = fields.BooleanField(_('Enable automatic reply'))\n    reply_subject = fields.StringField(_('Reply subject'))\n    reply_body = fields.StringField(_('Reply body'),\n        widget=widgets.TextArea())\n    reply_startdate = fields.DateField(_('Start of vacation'))\n    reply_enddate = fields.DateField(_('End of vacation'))\n    submit = fields.SubmitField(_('Update'))\n\n\nclass TokenForm(flask_wtf.FlaskForm):\n    displayed_password = fields.StringField(\n        _('Your token (write it down, as it will never be displayed again)')\n    )\n    raw_password = fields.HiddenField([validators.DataRequired()])\n    comment = fields.StringField(_('Comment'))\n    ip = fields.StringField(\n        _('Authorized IP'), [validators.Optional()]\n    )\n    submit = fields.SubmitField(_('Save'))\n\n    def validate_ip(form, field):\n        if not field.data:\n            return True\n        try:\n            for candidate in field.data.replace(' ','').split(','):\n                ipaddress.ip_network(candidate, False)\n        except:\n            raise validators.ValidationError('Not a valid list of CIDRs')\n\nclass AliasForm(flask_wtf.FlaskForm):\n    localpart = fields.StringField(_('Alias'), [validators.DataRequired(), validators.Regexp(LOCALPART_REGEX)])\n    wildcard = fields.BooleanField(\n        _('Use SQL LIKE Syntax (e.g. for catch-all aliases)'))\n    destination = DestinationField(_('Destination'))\n    comment = fields.StringField(_('Comment'))\n    submit = fields.SubmitField(_('Save'))\n\n\nclass AdminForm(flask_wtf.FlaskForm):\n    admin = fields.SelectField(_('Admin email'), choices=[])\n    submit = fields.SubmitField(_('Submit'))\n\n\nclass ManagerForm(flask_wtf.FlaskForm):\n    manager = fields.SelectField(_('Manager email'))\n    submit = fields.SubmitField(_('Submit'))\n\n\nclass FetchForm(flask_wtf.FlaskForm):\n    protocol = fields.SelectField(_('Protocol'), choices=[\n        ('imap', 'IMAP'), ('pop3', 'POP3')\n    ])\n    host = fields.StringField(_('Hostname or IP'), [validators.DataRequired()])\n    port = fields.IntegerField(_('TCP port'), [validators.DataRequired(), validators.NumberRange(min=0, max=65535)], default=993)\n    tls = fields.BooleanField(_('Enable TLS'), default=True)\n    username = fields.StringField(_('Username'), [validators.DataRequired()])\n    password = fields.PasswordField(_('Password'))\n    keep = fields.BooleanField(_('Keep emails on the server'))\n    scan = fields.BooleanField(_('Rescan emails locally'))\n    folders = fields.StringField(_('Folders to fetch on the server'), [validators.Optional(), MultipleFoldersVerify()], default='INBOX,Junk')\n    submit = fields.SubmitField(_('Submit'))\n\n\nclass AnnouncementForm(flask_wtf.FlaskForm):\n    announcement_subject = fields.StringField(_('Announcement subject'),\n        [validators.DataRequired()])\n    announcement_body = fields.StringField(_('Announcement body'),\n        [validators.DataRequired()], widget=widgets.TextArea())\n    submit = fields.SubmitField(_('Send'))\n", "path": "core/admin/mailu/ui/forms.py"}]}