We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Roundcube managesieve fails on IPv6
I have assigned a globally-routable IPv6 network to the docker-compose stack. Rainloop sieve management works correctly but Roundcube fails. Looking at the logs, Roundcube attempts to connect to sieve via IPv6. Hard-coding $imap to the IPv4 address of the imap docker container allows Roundcube sieve to function properly. I believe it is related to this in the debug log of imap:

`DEBUG:root:Table get passdb/[username redated] is {'allow_nets': '192.168.203.0/24', 'nopassword': 'Y', 'password': None}`

Using version 1.7.


--- 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/internal/views/dovecot.py`
Content:
```
1 from mailu import models
2 from mailu.internal import internal
3 from flask import current_app as app
4 
5 import flask
6 import socket
7 import os
8 
9 @internal.route("/dovecot/passdb/<path:user_email>")
10 def dovecot_passdb_dict(user_email):
11     user = models.User.query.get(user_email) or flask.abort(404)
12     allow_nets = []
13     allow_nets.append(app.config["SUBNET"])
14     if app.config["POD_ADDRESS_RANGE"]:
15         allow_nets.append(app.config["POD_ADDRESS_RANGE"])
16     return flask.jsonify({
17         "password": None,
18         "nopassword": "Y",
19         "allow_nets": ",".join(allow_nets)
20     })
21 
22 
23 @internal.route("/dovecot/userdb/<path:user_email>")
24 def dovecot_userdb_dict(user_email):
25     user = models.User.query.get(user_email) or flask.abort(404)
26     return flask.jsonify({
27         "quota_rule": "*:bytes={}".format(user.quota_bytes)
28     })
29 
30 
31 @internal.route("/dovecot/quota/<ns>/<path:user_email>", methods=["POST"])
32 def dovecot_quota(ns, user_email):
33     user = models.User.query.get(user_email) or flask.abort(404)
34     if ns == "storage":
35         user.quota_bytes_used = flask.request.get_json()
36         models.db.session.commit()
37     return flask.jsonify(None)
38 
39 
40 @internal.route("/dovecot/sieve/name/<script>/<path:user_email>")
41 def dovecot_sieve_name(script, user_email):
42     return flask.jsonify(script)
43 
44 
45 @internal.route("/dovecot/sieve/data/default/<path:user_email>")
46 def dovecot_sieve_data(user_email):
47     user = models.User.query.get(user_email) or flask.abort(404)
48     return flask.jsonify(flask.render_template("default.sieve", user=user))
49
```

Path: `core/admin/mailu/configuration.py`
Content:
```
1 import os
2 
3 from socrate import system
4 
5 DEFAULT_CONFIG = {
6     # Specific to the admin UI
7     'DOCKER_SOCKET': 'unix:///var/run/docker.sock',
8     'BABEL_DEFAULT_LOCALE': 'en',
9     'BABEL_DEFAULT_TIMEZONE': 'UTC',
10     'BOOTSTRAP_SERVE_LOCAL': True,
11     'RATELIMIT_STORAGE_URL': '',
12     'QUOTA_STORAGE_URL': '',
13     'DEBUG': False,
14     'DOMAIN_REGISTRATION': False,
15     'TEMPLATES_AUTO_RELOAD': True,
16     # Database settings
17     'DB_FLAVOR': None,
18     'DB_USER': 'mailu',
19     'DB_PW': None,
20     'DB_HOST': 'database',
21     'DB_NAME': 'mailu',
22     'SQLITE_DATABASE_FILE':'data/main.db',
23     'SQLALCHEMY_DATABASE_URI': 'sqlite:////data/main.db',
24     'SQLALCHEMY_TRACK_MODIFICATIONS': False,
25     # Statistics management
26     'INSTANCE_ID_PATH': '/data/instance',
27     'STATS_ENDPOINT': '0.{}.stats.mailu.io',
28     # Common configuration variables
29     'SECRET_KEY': 'changeMe',
30     'DOMAIN': 'mailu.io',
31     'HOSTNAMES': 'mail.mailu.io,alternative.mailu.io,yetanother.mailu.io',
32     'POSTMASTER': 'postmaster',
33     'TLS_FLAVOR': 'cert',
34     'AUTH_RATELIMIT': '10/minute;1000/hour',
35     'AUTH_RATELIMIT_SUBNET': True,
36     'DISABLE_STATISTICS': False,
37     # Mail settings
38     'DMARC_RUA': None,
39     'DMARC_RUF': None,
40     'WELCOME': False,
41     'WELCOME_SUBJECT': 'Dummy welcome topic',
42     'WELCOME_BODY': 'Dummy welcome body',
43     'DKIM_SELECTOR': 'dkim',
44     'DKIM_PATH': '/dkim/{domain}.{selector}.key',
45     'DEFAULT_QUOTA': 1000000000,
46     # Web settings
47     'SITENAME': 'Mailu',
48     'WEBSITE': 'https://mailu.io',
49     'WEB_ADMIN': '/admin',
50     'WEB_WEBMAIL': '/webmail',
51     'WEBMAIL': 'none',
52     'RECAPTCHA_PUBLIC_KEY': '',
53     'RECAPTCHA_PRIVATE_KEY': '',
54     # Advanced settings
55     'PASSWORD_SCHEME': 'PBKDF2',
56     'LOG_LEVEL': 'WARNING',
57     # Host settings
58     'HOST_IMAP': 'imap',
59     'HOST_LMTP': 'imap:2525',
60     'HOST_POP3': 'imap',
61     'HOST_SMTP': 'smtp',
62     'HOST_AUTHSMTP': 'smtp',
63     'HOST_ADMIN': 'admin',
64     'WEBMAIL': 'none',
65     'HOST_WEBMAIL': 'webmail',
66     'HOST_WEBDAV': 'webdav:5232',
67     'HOST_REDIS': 'redis',
68     'HOST_FRONT': 'front',
69     'SUBNET': '192.168.203.0/24',
70     'POD_ADDRESS_RANGE': None
71 }
72 
73 class ConfigManager(dict):
74     """ Naive configuration manager that uses environment only
75     """
76 
77     DB_TEMPLATES = {
78         'sqlite': 'sqlite:////{SQLITE_DATABASE_FILE}',
79         'postgresql': 'postgresql://{DB_USER}:{DB_PW}@{DB_HOST}/{DB_NAME}',
80         'mysql': 'mysql://{DB_USER}:{DB_PW}@{DB_HOST}/{DB_NAME}'
81     }
82 
83     def __init__(self):
84         self.config = dict()
85 
86     def get_host_address(self, name):
87         # if MYSERVICE_ADDRESS is defined, use this
88         if '{}_ADDRESS'.format(name) in os.environ:
89             return os.environ.get('{}_ADDRESS'.format(name))
90         # otherwise use the host name and resolve it
91         return system.resolve_address(self.config['HOST_{}'.format(name)])
92 
93     def resolve_hosts(self):
94         self.config["IMAP_ADDRESS"] = self.get_host_address("IMAP")
95         self.config["POP3_ADDRESS"] = self.get_host_address("POP3")
96         self.config["AUTHSMTP_ADDRESS"] = self.get_host_address("AUTHSMTP")
97         self.config["SMTP_ADDRESS"] = self.get_host_address("SMTP")
98         self.config["REDIS_ADDRESS"] = self.get_host_address("REDIS")
99         if self.config["WEBMAIL"] != "none":
100             self.config["WEBMAIL_ADDRESS"] = self.get_host_address("WEBMAIL")
101 
102     def __coerce_value(self, value):
103         if isinstance(value, str) and value.lower() in ('true','yes'):
104             return True
105         elif isinstance(value, str) and value.lower() in ('false', 'no'):
106             return False
107         return value
108 
109     def init_app(self, app):
110         self.config.update(app.config)
111         # get environment variables
112         self.config.update({
113             key: self.__coerce_value(os.environ.get(key, value))
114             for key, value in DEFAULT_CONFIG.items()
115         })
116         self.resolve_hosts()
117 
118         # automatically set the sqlalchemy string
119         if self.config['DB_FLAVOR']:
120             template = self.DB_TEMPLATES[self.config['DB_FLAVOR']]
121             self.config['SQLALCHEMY_DATABASE_URI'] = template.format(**self.config)
122 
123         self.config['RATELIMIT_STORAGE_URL'] = 'redis://{0}/2'.format(self.config['REDIS_ADDRESS'])
124         self.config['QUOTA_STORAGE_URL'] = 'redis://{0}/1'.format(self.config['REDIS_ADDRESS'])
125         # update the app config itself
126         app.config = self
127 
128     def setdefault(self, key, value):
129         if key not in self.config:
130             self.config[key] = value
131         return self.config[key]
132 
133     def get(self, *args):
134         return self.config.get(*args)
135 
136     def keys(self):
137         return self.config.keys()
138 
139     def __getitem__(self, key):
140         return self.config.get(key)
141 
142     def __setitem__(self, key, value):
143         self.config[key] = value
144 
145     def __contains__(self, key):
146         return key in self.config
147
```
--- 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/configuration.py b/core/admin/mailu/configuration.py
--- a/core/admin/mailu/configuration.py
+++ b/core/admin/mailu/configuration.py
@@ -67,6 +67,7 @@
     'HOST_REDIS': 'redis',
     'HOST_FRONT': 'front',
     'SUBNET': '192.168.203.0/24',
+    'SUBNET6': None,
     'POD_ADDRESS_RANGE': None
 }
 
diff --git a/core/admin/mailu/internal/views/dovecot.py b/core/admin/mailu/internal/views/dovecot.py
--- a/core/admin/mailu/internal/views/dovecot.py
+++ b/core/admin/mailu/internal/views/dovecot.py
@@ -11,6 +11,8 @@
     user = models.User.query.get(user_email) or flask.abort(404)
     allow_nets = []
     allow_nets.append(app.config["SUBNET"])
+    if app.config["SUBNET6"]:
+        allow_nets.append(app.config["SUBNET6"])
     if app.config["POD_ADDRESS_RANGE"]:
         allow_nets.append(app.config["POD_ADDRESS_RANGE"])
     return flask.jsonify({

{"golden_diff": "diff --git a/core/admin/mailu/configuration.py b/core/admin/mailu/configuration.py\n--- a/core/admin/mailu/configuration.py\n+++ b/core/admin/mailu/configuration.py\n@@ -67,6 +67,7 @@\n     'HOST_REDIS': 'redis',\n     'HOST_FRONT': 'front',\n     'SUBNET': '192.168.203.0/24',\n+    'SUBNET6': None,\n     'POD_ADDRESS_RANGE': None\n }\n \ndiff --git a/core/admin/mailu/internal/views/dovecot.py b/core/admin/mailu/internal/views/dovecot.py\n--- a/core/admin/mailu/internal/views/dovecot.py\n+++ b/core/admin/mailu/internal/views/dovecot.py\n@@ -11,6 +11,8 @@\n     user = models.User.query.get(user_email) or flask.abort(404)\n     allow_nets = []\n     allow_nets.append(app.config[\"SUBNET\"])\n+    if app.config[\"SUBNET6\"]:\n+        allow_nets.append(app.config[\"SUBNET6\"])\n     if app.config[\"POD_ADDRESS_RANGE\"]:\n         allow_nets.append(app.config[\"POD_ADDRESS_RANGE\"])\n     return flask.jsonify({\n", "issue": "Roundcube managesieve fails on IPv6\nI have assigned a globally-routable IPv6 network to the docker-compose stack. Rainloop sieve management works correctly but Roundcube fails. Looking at the logs, Roundcube attempts to connect to sieve via IPv6. Hard-coding $imap to the IPv4 address of the imap docker container allows Roundcube sieve to function properly. I believe it is related to this in the debug log of imap:\r\n\r\n`DEBUG:root:Table get passdb/[username redated] is {'allow_nets': '192.168.203.0/24', 'nopassword': 'Y', 'password': None}`\r\n\r\nUsing version 1.7.\r\n\n", "before_files": [{"content": "from mailu import models\nfrom mailu.internal import internal\nfrom flask import current_app as app\n\nimport flask\nimport socket\nimport os\n\n@internal.route(\"/dovecot/passdb/<path:user_email>\")\ndef dovecot_passdb_dict(user_email):\n    user = models.User.query.get(user_email) or flask.abort(404)\n    allow_nets = []\n    allow_nets.append(app.config[\"SUBNET\"])\n    if app.config[\"POD_ADDRESS_RANGE\"]:\n        allow_nets.append(app.config[\"POD_ADDRESS_RANGE\"])\n    return flask.jsonify({\n        \"password\": None,\n        \"nopassword\": \"Y\",\n        \"allow_nets\": \",\".join(allow_nets)\n    })\n\n\n@internal.route(\"/dovecot/userdb/<path:user_email>\")\ndef dovecot_userdb_dict(user_email):\n    user = models.User.query.get(user_email) or flask.abort(404)\n    return flask.jsonify({\n        \"quota_rule\": \"*:bytes={}\".format(user.quota_bytes)\n    })\n\n\n@internal.route(\"/dovecot/quota/<ns>/<path:user_email>\", methods=[\"POST\"])\ndef dovecot_quota(ns, user_email):\n    user = models.User.query.get(user_email) or flask.abort(404)\n    if ns == \"storage\":\n        user.quota_bytes_used = flask.request.get_json()\n        models.db.session.commit()\n    return flask.jsonify(None)\n\n\n@internal.route(\"/dovecot/sieve/name/<script>/<path:user_email>\")\ndef dovecot_sieve_name(script, user_email):\n    return flask.jsonify(script)\n\n\n@internal.route(\"/dovecot/sieve/data/default/<path:user_email>\")\ndef dovecot_sieve_data(user_email):\n    user = models.User.query.get(user_email) or flask.abort(404)\n    return flask.jsonify(flask.render_template(\"default.sieve\", user=user))\n", "path": "core/admin/mailu/internal/views/dovecot.py"}, {"content": "import os\n\nfrom socrate import system\n\nDEFAULT_CONFIG = {\n    # Specific to the admin UI\n    'DOCKER_SOCKET': 'unix:///var/run/docker.sock',\n    'BABEL_DEFAULT_LOCALE': 'en',\n    'BABEL_DEFAULT_TIMEZONE': 'UTC',\n    'BOOTSTRAP_SERVE_LOCAL': True,\n    'RATELIMIT_STORAGE_URL': '',\n    'QUOTA_STORAGE_URL': '',\n    'DEBUG': False,\n    'DOMAIN_REGISTRATION': False,\n    'TEMPLATES_AUTO_RELOAD': True,\n    # Database settings\n    'DB_FLAVOR': None,\n    'DB_USER': 'mailu',\n    'DB_PW': None,\n    'DB_HOST': 'database',\n    'DB_NAME': 'mailu',\n    'SQLITE_DATABASE_FILE':'data/main.db',\n    'SQLALCHEMY_DATABASE_URI': 'sqlite:////data/main.db',\n    'SQLALCHEMY_TRACK_MODIFICATIONS': False,\n    # Statistics management\n    'INSTANCE_ID_PATH': '/data/instance',\n    'STATS_ENDPOINT': '0.{}.stats.mailu.io',\n    # Common configuration variables\n    'SECRET_KEY': 'changeMe',\n    'DOMAIN': 'mailu.io',\n    'HOSTNAMES': 'mail.mailu.io,alternative.mailu.io,yetanother.mailu.io',\n    'POSTMASTER': 'postmaster',\n    'TLS_FLAVOR': 'cert',\n    'AUTH_RATELIMIT': '10/minute;1000/hour',\n    'AUTH_RATELIMIT_SUBNET': True,\n    'DISABLE_STATISTICS': False,\n    # Mail settings\n    'DMARC_RUA': None,\n    'DMARC_RUF': None,\n    'WELCOME': False,\n    'WELCOME_SUBJECT': 'Dummy welcome topic',\n    'WELCOME_BODY': 'Dummy welcome body',\n    'DKIM_SELECTOR': 'dkim',\n    'DKIM_PATH': '/dkim/{domain}.{selector}.key',\n    'DEFAULT_QUOTA': 1000000000,\n    # Web settings\n    'SITENAME': 'Mailu',\n    'WEBSITE': 'https://mailu.io',\n    'WEB_ADMIN': '/admin',\n    'WEB_WEBMAIL': '/webmail',\n    'WEBMAIL': 'none',\n    'RECAPTCHA_PUBLIC_KEY': '',\n    'RECAPTCHA_PRIVATE_KEY': '',\n    # Advanced settings\n    'PASSWORD_SCHEME': 'PBKDF2',\n    'LOG_LEVEL': 'WARNING',\n    # Host settings\n    'HOST_IMAP': 'imap',\n    'HOST_LMTP': 'imap:2525',\n    'HOST_POP3': 'imap',\n    'HOST_SMTP': 'smtp',\n    'HOST_AUTHSMTP': 'smtp',\n    'HOST_ADMIN': 'admin',\n    'WEBMAIL': 'none',\n    'HOST_WEBMAIL': 'webmail',\n    'HOST_WEBDAV': 'webdav:5232',\n    'HOST_REDIS': 'redis',\n    'HOST_FRONT': 'front',\n    'SUBNET': '192.168.203.0/24',\n    'POD_ADDRESS_RANGE': None\n}\n\nclass ConfigManager(dict):\n    \"\"\" Naive configuration manager that uses environment only\n    \"\"\"\n\n    DB_TEMPLATES = {\n        'sqlite': 'sqlite:////{SQLITE_DATABASE_FILE}',\n        'postgresql': 'postgresql://{DB_USER}:{DB_PW}@{DB_HOST}/{DB_NAME}',\n        'mysql': 'mysql://{DB_USER}:{DB_PW}@{DB_HOST}/{DB_NAME}'\n    }\n\n    def __init__(self):\n        self.config = dict()\n\n    def get_host_address(self, name):\n        # if MYSERVICE_ADDRESS is defined, use this\n        if '{}_ADDRESS'.format(name) in os.environ:\n            return os.environ.get('{}_ADDRESS'.format(name))\n        # otherwise use the host name and resolve it\n        return system.resolve_address(self.config['HOST_{}'.format(name)])\n\n    def resolve_hosts(self):\n        self.config[\"IMAP_ADDRESS\"] = self.get_host_address(\"IMAP\")\n        self.config[\"POP3_ADDRESS\"] = self.get_host_address(\"POP3\")\n        self.config[\"AUTHSMTP_ADDRESS\"] = self.get_host_address(\"AUTHSMTP\")\n        self.config[\"SMTP_ADDRESS\"] = self.get_host_address(\"SMTP\")\n        self.config[\"REDIS_ADDRESS\"] = self.get_host_address(\"REDIS\")\n        if self.config[\"WEBMAIL\"] != \"none\":\n            self.config[\"WEBMAIL_ADDRESS\"] = self.get_host_address(\"WEBMAIL\")\n\n    def __coerce_value(self, value):\n        if isinstance(value, str) and value.lower() in ('true','yes'):\n            return True\n        elif isinstance(value, str) and value.lower() in ('false', 'no'):\n            return False\n        return value\n\n    def init_app(self, app):\n        self.config.update(app.config)\n        # get environment variables\n        self.config.update({\n            key: self.__coerce_value(os.environ.get(key, value))\n            for key, value in DEFAULT_CONFIG.items()\n        })\n        self.resolve_hosts()\n\n        # automatically set the sqlalchemy string\n        if self.config['DB_FLAVOR']:\n            template = self.DB_TEMPLATES[self.config['DB_FLAVOR']]\n            self.config['SQLALCHEMY_DATABASE_URI'] = template.format(**self.config)\n\n        self.config['RATELIMIT_STORAGE_URL'] = 'redis://{0}/2'.format(self.config['REDIS_ADDRESS'])\n        self.config['QUOTA_STORAGE_URL'] = 'redis://{0}/1'.format(self.config['REDIS_ADDRESS'])\n        # update the app config itself\n        app.config = self\n\n    def setdefault(self, key, value):\n        if key not in self.config:\n            self.config[key] = value\n        return self.config[key]\n\n    def get(self, *args):\n        return self.config.get(*args)\n\n    def keys(self):\n        return self.config.keys()\n\n    def __getitem__(self, key):\n        return self.config.get(key)\n\n    def __setitem__(self, key, value):\n        self.config[key] = value\n\n    def __contains__(self, key):\n        return key in self.config\n", "path": "core/admin/mailu/configuration.py"}], "after_files": [{"content": "from mailu import models\nfrom mailu.internal import internal\nfrom flask import current_app as app\n\nimport flask\nimport socket\nimport os\n\n@internal.route(\"/dovecot/passdb/<path:user_email>\")\ndef dovecot_passdb_dict(user_email):\n    user = models.User.query.get(user_email) or flask.abort(404)\n    allow_nets = []\n    allow_nets.append(app.config[\"SUBNET\"])\n    if app.config[\"SUBNET6\"]:\n        allow_nets.append(app.config[\"SUBNET6\"])\n    if app.config[\"POD_ADDRESS_RANGE\"]:\n        allow_nets.append(app.config[\"POD_ADDRESS_RANGE\"])\n    return flask.jsonify({\n        \"password\": None,\n        \"nopassword\": \"Y\",\n        \"allow_nets\": \",\".join(allow_nets)\n    })\n\n\n@internal.route(\"/dovecot/userdb/<path:user_email>\")\ndef dovecot_userdb_dict(user_email):\n    user = models.User.query.get(user_email) or flask.abort(404)\n    return flask.jsonify({\n        \"quota_rule\": \"*:bytes={}\".format(user.quota_bytes)\n    })\n\n\n@internal.route(\"/dovecot/quota/<ns>/<path:user_email>\", methods=[\"POST\"])\ndef dovecot_quota(ns, user_email):\n    user = models.User.query.get(user_email) or flask.abort(404)\n    if ns == \"storage\":\n        user.quota_bytes_used = flask.request.get_json()\n        models.db.session.commit()\n    return flask.jsonify(None)\n\n\n@internal.route(\"/dovecot/sieve/name/<script>/<path:user_email>\")\ndef dovecot_sieve_name(script, user_email):\n    return flask.jsonify(script)\n\n\n@internal.route(\"/dovecot/sieve/data/default/<path:user_email>\")\ndef dovecot_sieve_data(user_email):\n    user = models.User.query.get(user_email) or flask.abort(404)\n    return flask.jsonify(flask.render_template(\"default.sieve\", user=user))\n", "path": "core/admin/mailu/internal/views/dovecot.py"}, {"content": "import os\n\nfrom socrate import system\n\nDEFAULT_CONFIG = {\n    # Specific to the admin UI\n    'DOCKER_SOCKET': 'unix:///var/run/docker.sock',\n    'BABEL_DEFAULT_LOCALE': 'en',\n    'BABEL_DEFAULT_TIMEZONE': 'UTC',\n    'BOOTSTRAP_SERVE_LOCAL': True,\n    'RATELIMIT_STORAGE_URL': '',\n    'QUOTA_STORAGE_URL': '',\n    'DEBUG': False,\n    'DOMAIN_REGISTRATION': False,\n    'TEMPLATES_AUTO_RELOAD': True,\n    # Database settings\n    'DB_FLAVOR': None,\n    'DB_USER': 'mailu',\n    'DB_PW': None,\n    'DB_HOST': 'database',\n    'DB_NAME': 'mailu',\n    'SQLITE_DATABASE_FILE':'data/main.db',\n    'SQLALCHEMY_DATABASE_URI': 'sqlite:////data/main.db',\n    'SQLALCHEMY_TRACK_MODIFICATIONS': False,\n    # Statistics management\n    'INSTANCE_ID_PATH': '/data/instance',\n    'STATS_ENDPOINT': '0.{}.stats.mailu.io',\n    # Common configuration variables\n    'SECRET_KEY': 'changeMe',\n    'DOMAIN': 'mailu.io',\n    'HOSTNAMES': 'mail.mailu.io,alternative.mailu.io,yetanother.mailu.io',\n    'POSTMASTER': 'postmaster',\n    'TLS_FLAVOR': 'cert',\n    'AUTH_RATELIMIT': '10/minute;1000/hour',\n    'AUTH_RATELIMIT_SUBNET': True,\n    'DISABLE_STATISTICS': False,\n    # Mail settings\n    'DMARC_RUA': None,\n    'DMARC_RUF': None,\n    'WELCOME': False,\n    'WELCOME_SUBJECT': 'Dummy welcome topic',\n    'WELCOME_BODY': 'Dummy welcome body',\n    'DKIM_SELECTOR': 'dkim',\n    'DKIM_PATH': '/dkim/{domain}.{selector}.key',\n    'DEFAULT_QUOTA': 1000000000,\n    # Web settings\n    'SITENAME': 'Mailu',\n    'WEBSITE': 'https://mailu.io',\n    'WEB_ADMIN': '/admin',\n    'WEB_WEBMAIL': '/webmail',\n    'WEBMAIL': 'none',\n    'RECAPTCHA_PUBLIC_KEY': '',\n    'RECAPTCHA_PRIVATE_KEY': '',\n    # Advanced settings\n    'PASSWORD_SCHEME': 'PBKDF2',\n    'LOG_LEVEL': 'WARNING',\n    # Host settings\n    'HOST_IMAP': 'imap',\n    'HOST_LMTP': 'imap:2525',\n    'HOST_POP3': 'imap',\n    'HOST_SMTP': 'smtp',\n    'HOST_AUTHSMTP': 'smtp',\n    'HOST_ADMIN': 'admin',\n    'WEBMAIL': 'none',\n    'HOST_WEBMAIL': 'webmail',\n    'HOST_WEBDAV': 'webdav:5232',\n    'HOST_REDIS': 'redis',\n    'HOST_FRONT': 'front',\n    'SUBNET': '192.168.203.0/24',\n    'SUBNET6': None,\n    'POD_ADDRESS_RANGE': None\n}\n\nclass ConfigManager(dict):\n    \"\"\" Naive configuration manager that uses environment only\n    \"\"\"\n\n    DB_TEMPLATES = {\n        'sqlite': 'sqlite:////{SQLITE_DATABASE_FILE}',\n        'postgresql': 'postgresql://{DB_USER}:{DB_PW}@{DB_HOST}/{DB_NAME}',\n        'mysql': 'mysql://{DB_USER}:{DB_PW}@{DB_HOST}/{DB_NAME}'\n    }\n\n    def __init__(self):\n        self.config = dict()\n\n    def get_host_address(self, name):\n        # if MYSERVICE_ADDRESS is defined, use this\n        if '{}_ADDRESS'.format(name) in os.environ:\n            return os.environ.get('{}_ADDRESS'.format(name))\n        # otherwise use the host name and resolve it\n        return system.resolve_address(self.config['HOST_{}'.format(name)])\n\n    def resolve_hosts(self):\n        self.config[\"IMAP_ADDRESS\"] = self.get_host_address(\"IMAP\")\n        self.config[\"POP3_ADDRESS\"] = self.get_host_address(\"POP3\")\n        self.config[\"AUTHSMTP_ADDRESS\"] = self.get_host_address(\"AUTHSMTP\")\n        self.config[\"SMTP_ADDRESS\"] = self.get_host_address(\"SMTP\")\n        self.config[\"REDIS_ADDRESS\"] = self.get_host_address(\"REDIS\")\n        if self.config[\"WEBMAIL\"] != \"none\":\n            self.config[\"WEBMAIL_ADDRESS\"] = self.get_host_address(\"WEBMAIL\")\n\n    def __coerce_value(self, value):\n        if isinstance(value, str) and value.lower() in ('true','yes'):\n            return True\n        elif isinstance(value, str) and value.lower() in ('false', 'no'):\n            return False\n        return value\n\n    def init_app(self, app):\n        self.config.update(app.config)\n        # get environment variables\n        self.config.update({\n            key: self.__coerce_value(os.environ.get(key, value))\n            for key, value in DEFAULT_CONFIG.items()\n        })\n        self.resolve_hosts()\n\n        # automatically set the sqlalchemy string\n        if self.config['DB_FLAVOR']:\n            template = self.DB_TEMPLATES[self.config['DB_FLAVOR']]\n            self.config['SQLALCHEMY_DATABASE_URI'] = template.format(**self.config)\n\n        self.config['RATELIMIT_STORAGE_URL'] = 'redis://{0}/2'.format(self.config['REDIS_ADDRESS'])\n        self.config['QUOTA_STORAGE_URL'] = 'redis://{0}/1'.format(self.config['REDIS_ADDRESS'])\n        # update the app config itself\n        app.config = self\n\n    def setdefault(self, key, value):\n        if key not in self.config:\n            self.config[key] = value\n        return self.config[key]\n\n    def get(self, *args):\n        return self.config.get(*args)\n\n    def keys(self):\n        return self.config.keys()\n\n    def __getitem__(self, key):\n        return self.config.get(key)\n\n    def __setitem__(self, key, value):\n        self.config[key] = value\n\n    def __contains__(self, key):\n        return key in self.config\n", "path": "core/admin/mailu/configuration.py"}]}