From ebbfd486f749b13505f5565ea004b5120507ed94 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Thu, 5 Sep 2024 21:58:11 +0200 Subject: [PATCH 01/13] fix #3405 --- docs/maintain.rst | 1 + towncrier/newsfragments/3405.bugfix | 1 + 2 files changed, 2 insertions(+) create mode 100644 towncrier/newsfragments/3405.bugfix diff --git a/docs/maintain.rst b/docs/maintain.rst index eff8f5c4..a2041a86 100644 --- a/docs/maintain.rst +++ b/docs/maintain.rst @@ -47,6 +47,7 @@ In the case of *certbot* you could write a script to be executed as `deploy hook cp /etc/letsencrypt/live/domain.com/privkey.pem /mailu/certs/key.pem || exit 1 cp /etc/letsencrypt/live/domain.com/fullchain.pem /mailu/certs/cert.pem || exit 1 docker exec mailu_front_1 nginx -s reload + docker exec mailu_front_1 doveadm reload And the certbot command you will use in crontab would look something like: diff --git a/towncrier/newsfragments/3405.bugfix b/towncrier/newsfragments/3405.bugfix new file mode 100644 index 00000000..07db93a8 --- /dev/null +++ b/towncrier/newsfragments/3405.bugfix @@ -0,0 +1 @@ +Update the documentation: ensure that users reload dovecot too if they manually configure certificates From 4af085253eda5911e448881b821e44c7df051529 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Thu, 5 Sep 2024 22:18:12 +0200 Subject: [PATCH 02/13] fix #3401 --- webmails/snuffleupagus.rules | 1 + 1 file changed, 1 insertion(+) diff --git a/webmails/snuffleupagus.rules b/webmails/snuffleupagus.rules index 10390d45..3d4713f8 100644 --- a/webmails/snuffleupagus.rules +++ b/webmails/snuffleupagus.rules @@ -130,6 +130,7 @@ sp.disable_function.function("move_uploaded_file").param("to").value_r("\\.ht"). # Logging lockdown sp.disable_function.function("ini_set").param("option").value_r("error_log").drop() +sp.disable_function.function("ini_set").param("option").value_r("display_errors").filename_r("/var/www/snappymail/snappymail/v/[0-9]+\.[0-9]+\.[0-9]+/app/libraries/snappymail/shutdown.php").allow(); sp.disable_function.function("ini_set").param("option").value_r("display_errors").drop() sp.auto_cookie_secure.enable(); From e5790a297aa3cfaf0092970ea3716e6cb1eda7f9 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Thu, 5 Sep 2024 22:33:23 +0200 Subject: [PATCH 03/13] fix #3403 --- core/admin/mailu/internal/nginx.py | 3 +++ towncrier/newsfragments/3403.bugfix | 1 + 2 files changed, 4 insertions(+) create mode 100644 towncrier/newsfragments/3403.bugfix diff --git a/core/admin/mailu/internal/nginx.py b/core/admin/mailu/internal/nginx.py index daef8b9e..9cf9cfb0 100644 --- a/core/admin/mailu/internal/nginx.py +++ b/core/admin/mailu/internal/nginx.py @@ -19,6 +19,9 @@ STATUSES = { }), "encryption": ("Must issue a STARTTLS command first", { "smtp": "530 5.7.0" + "submission": "530 5.7.0", + "pop3": "-ERR Authentication canceled.", + "sieve": "ENCRYPT-NEEDED" }), "ratelimit": ("Temporary authentication failure (rate-limit)", { "imap": "LIMIT", diff --git a/towncrier/newsfragments/3403.bugfix b/towncrier/newsfragments/3403.bugfix new file mode 100644 index 00000000..d8169ba1 --- /dev/null +++ b/towncrier/newsfragments/3403.bugfix @@ -0,0 +1 @@ +fix INBOUND_TLS_ENFORCE From b426c679494ca5e562679b331e3402a7637491d0 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Thu, 5 Sep 2024 22:37:30 +0200 Subject: [PATCH 04/13] doh --- core/admin/mailu/internal/nginx.py | 1 + 1 file changed, 1 insertion(+) diff --git a/core/admin/mailu/internal/nginx.py b/core/admin/mailu/internal/nginx.py index 9cf9cfb0..6c320ae4 100644 --- a/core/admin/mailu/internal/nginx.py +++ b/core/admin/mailu/internal/nginx.py @@ -18,6 +18,7 @@ STATUSES = { "sieve": "AuthFailed" }), "encryption": ("Must issue a STARTTLS command first", { + "imap": "PRIVACYREQUIRED", "smtp": "530 5.7.0" "submission": "530 5.7.0", "pop3": "-ERR Authentication canceled.", From bae4855bea381a7d6f202386da0c08eea9edb306 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Thu, 5 Sep 2024 22:45:25 +0200 Subject: [PATCH 05/13] fix #3379 --- core/admin/mailu/ui/views/users.py | 2 +- towncrier/newsfragments/3379.bugfix | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 towncrier/newsfragments/3379.bugfix diff --git a/core/admin/mailu/ui/views/users.py b/core/admin/mailu/ui/views/users.py index 6487c326..85f7c577 100644 --- a/core/admin/mailu/ui/views/users.py +++ b/core/admin/mailu/ui/views/users.py @@ -24,7 +24,7 @@ def user_create(domain_name): flask.url_for('.user_list', domain_name=domain.name)) form = forms.UserForm() form.pw.validators = [wtforms.validators.DataRequired()] - form.quota_bytes.default = app.config['DEFAULT_QUOTA'] + form.quota_bytes.default = int(app.config['DEFAULT_QUOTA']) if domain.max_quota_bytes: form.quota_bytes.validators = [ wtforms.validators.NumberRange(max=domain.max_quota_bytes)] diff --git a/towncrier/newsfragments/3379.bugfix b/towncrier/newsfragments/3379.bugfix new file mode 100644 index 00000000..c1842c52 --- /dev/null +++ b/towncrier/newsfragments/3379.bugfix @@ -0,0 +1 @@ +Fix #3379: DEFAULT_QUOTA From 61812ac32a96b06497e2d646a60b0a2f349f1937 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Thu, 5 Sep 2024 22:57:35 +0200 Subject: [PATCH 06/13] Untested code that may fix the following: Exception in thread Thread-2 (forward_text_lines): Traceback (most recent call last): File "/usr/lib/python3.12/threading.py", line 1073, in _bootstrap_inner self.run() File "/usr/lib/python3.12/threading.py", line 1010, in run self._target(*self._args, **self._kwargs) File "/app/venv/lib/python3.12/site-packages/socrate/system.py", line 155, in forward_text_lines current_line = src.readline() ^^^^^^^^^^^^^^ File "", line 322, in decode UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa8 in position 166: invalid start byte This was reported in #3398 --- core/base/libs/socrate/socrate/system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/base/libs/socrate/socrate/system.py b/core/base/libs/socrate/socrate/system.py index 8ea770a3..c5046f4c 100644 --- a/core/base/libs/socrate/socrate/system.py +++ b/core/base/libs/socrate/socrate/system.py @@ -158,7 +158,7 @@ def forward_text_lines(src, dst): # runs a process and passes its standard/error output to the standard/error output of the current python script def run_process_and_forward_output(cmd): - process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, encoding='utf-8') stdout_thread = threading.Thread(target=forward_text_lines, args=(process.stdout, sys.stdout)) stdout_thread.daemon = True From 0021ba9cb9a52d70ee0e71d369e1ae7df2ab016f Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Thu, 5 Sep 2024 23:08:41 +0200 Subject: [PATCH 07/13] fix #3272 --- towncrier/newsfragments/3272.bugfix | 1 + webmails/nginx-webmail.conf | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 towncrier/newsfragments/3272.bugfix diff --git a/towncrier/newsfragments/3272.bugfix b/towncrier/newsfragments/3272.bugfix new file mode 100644 index 00000000..726e9e78 --- /dev/null +++ b/towncrier/newsfragments/3272.bugfix @@ -0,0 +1 @@ +Increase the size of buffers for webmail diff --git a/webmails/nginx-webmail.conf b/webmails/nginx-webmail.conf index 8772c8c8..d403eea4 100644 --- a/webmails/nginx-webmail.conf +++ b/webmails/nginx-webmail.conf @@ -55,6 +55,16 @@ server { {% else %} fastcgi_param SCRIPT_NAME {{WEB_WEBMAIL}}/$fastcgi_script_name; {% endif %} + + # fastcgi buffers for php-fpm # + fastcgi_buffers 16 32k; + fastcgi_buffer_size 64k; + fastcgi_busy_buffers_size 64k; + + # nginx buffers # + proxy_buffer_size 128k; + proxy_buffers 4 256k; + proxy_busy_buffers_size 256k; } location ~ (^|/)\. { From 5ea3840fc47029f6d5cc41eb3ac41a155213806b Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Thu, 5 Sep 2024 23:22:55 +0200 Subject: [PATCH 08/13] doh --- core/admin/mailu/internal/nginx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/admin/mailu/internal/nginx.py b/core/admin/mailu/internal/nginx.py index 6c320ae4..cf24e12f 100644 --- a/core/admin/mailu/internal/nginx.py +++ b/core/admin/mailu/internal/nginx.py @@ -19,7 +19,7 @@ STATUSES = { }), "encryption": ("Must issue a STARTTLS command first", { "imap": "PRIVACYREQUIRED", - "smtp": "530 5.7.0" + "smtp": "530 5.7.0", "submission": "530 5.7.0", "pop3": "-ERR Authentication canceled.", "sieve": "ENCRYPT-NEEDED" From 97e21df84573c24682693095d0b9bb21b7ce9b5b Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Fri, 6 Sep 2024 09:34:34 +0200 Subject: [PATCH 09/13] towncrier --- towncrier/newsfragments/3401.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 towncrier/newsfragments/3401.bugfix diff --git a/towncrier/newsfragments/3401.bugfix b/towncrier/newsfragments/3401.bugfix new file mode 100644 index 00000000..5309408d --- /dev/null +++ b/towncrier/newsfragments/3401.bugfix @@ -0,0 +1 @@ +Fix an error that can occur when using snappymail From 28c28b1c84fb8699fed89fc9fc1519051f32d0d4 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Fri, 6 Sep 2024 09:40:26 +0200 Subject: [PATCH 10/13] INBOUND_TLS_ENFORCE makes no sense. --- core/admin/mailu/internal/nginx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/admin/mailu/internal/nginx.py b/core/admin/mailu/internal/nginx.py index cf24e12f..b9cbe879 100644 --- a/core/admin/mailu/internal/nginx.py +++ b/core/admin/mailu/internal/nginx.py @@ -72,7 +72,7 @@ def handle_authentication(headers): # Incoming mail, no authentication if method in ['', 'none'] and protocol in ['smtp', 'lmtp']: server, port = get_server(protocol, False) - if app.config["INBOUND_TLS_ENFORCE"]: + if app.config["INBOUND_TLS_ENFORCE"] and protocol == 'smtp': if "Auth-SSL" in headers and headers["Auth-SSL"] == "on": return { "Auth-Status": "OK", From 61b444f00f900e7c41a5f7314f6a04be38e87b5e Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Fri, 6 Sep 2024 13:56:32 +0200 Subject: [PATCH 11/13] fix #2996 --- optional/fetchmail/fetchmail.py | 18 +++++++++++++++++- towncrier/newsfragments/2296.bugfix | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 towncrier/newsfragments/2296.bugfix diff --git a/optional/fetchmail/fetchmail.py b/optional/fetchmail/fetchmail.py index 410ce4fb..8e26e82e 100755 --- a/optional/fetchmail/fetchmail.py +++ b/optional/fetchmail/fetchmail.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +import base64 import time import os from pathlib import Path @@ -32,6 +33,21 @@ poll "{host}" proto {protocol} port {port} {lmtp} """ +def imaputf7encode(s): + """"Encode a string into RFC2060 aka IMAP UTF7""" + s=s.replace('&','&-') + iters=iter(s) + unipart=out='' + for c in s: + if 0x20<=ord(c)<=0x7f : + if unipart!='' : + out+='&'+base64.b64encode(unipart.encode('utf-16-be')).decode('ascii').rstrip('=')+'-' + unipart='' + out+=c + else : unipart+=c + if unipart!='' : + out+='&'+base64.b64encode(unipart.encode('utf-16-be')).decode('ascii').rstrip('=')+'-' + return out def escape_rc_string(arg): return "".join("\\x%2x" % ord(char) for char in arg) @@ -54,7 +70,7 @@ def run(debug): options = "options antispam 501, 504, 550, 553, 554" options += " ssl" if fetch["tls"] else "" options += " keep" if fetch["keep"] else " fetchall" - folders = "folders %s" % ((','.join('"' + item + '"' for item in fetch['folders'])) if fetch['folders'] else '"INBOX"') + folders = "folders %s" % ((','.join('"' + imaputf7encode(item) + '"' for item in fetch['folders'])) if fetch['folders'] else '"INBOX"') fetchmailrc += RC_LINE.format( user_email=escape_rc_string(fetch["user_email"]), protocol=fetch["protocol"], diff --git a/towncrier/newsfragments/2296.bugfix b/towncrier/newsfragments/2296.bugfix new file mode 100644 index 00000000..493238ff --- /dev/null +++ b/towncrier/newsfragments/2296.bugfix @@ -0,0 +1 @@ +Ensure fetchmail can deal with special characters in folder names From d70e82765f55a9c0f8a55af6226dc50801dbfb7d Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sat, 7 Sep 2024 12:57:42 +0200 Subject: [PATCH 12/13] as per review Co-authored-by: Alexander Graf --- optional/fetchmail/fetchmail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optional/fetchmail/fetchmail.py b/optional/fetchmail/fetchmail.py index 8e26e82e..5bd47cd7 100755 --- a/optional/fetchmail/fetchmail.py +++ b/optional/fetchmail/fetchmail.py @@ -70,7 +70,7 @@ def run(debug): options = "options antispam 501, 504, 550, 553, 554" options += " ssl" if fetch["tls"] else "" options += " keep" if fetch["keep"] else " fetchall" - folders = "folders %s" % ((','.join('"' + imaputf7encode(item) + '"' for item in fetch['folders'])) if fetch['folders'] else '"INBOX"') + folders = f"folders {",".join(f'"{imaputf7encode(item).replace('"',r"\34")}"' for item in fetch["folders"]) or '"INBOX"'}" fetchmailrc += RC_LINE.format( user_email=escape_rc_string(fetch["user_email"]), protocol=fetch["protocol"], From 2b9ba9ba3a75b8c1e58bb62ea159f05c1f366f91 Mon Sep 17 00:00:00 2001 From: Florent Daigniere Date: Sat, 7 Sep 2024 19:03:02 +0200 Subject: [PATCH 13/13] as per review --- optional/fetchmail/fetchmail.py | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/optional/fetchmail/fetchmail.py b/optional/fetchmail/fetchmail.py index 5bd47cd7..96f387dd 100755 --- a/optional/fetchmail/fetchmail.py +++ b/optional/fetchmail/fetchmail.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -import base64 +import binascii import time import os from pathlib import Path @@ -34,20 +34,18 @@ poll "{host}" proto {protocol} port {port} """ def imaputf7encode(s): - """"Encode a string into RFC2060 aka IMAP UTF7""" - s=s.replace('&','&-') - iters=iter(s) - unipart=out='' - for c in s: - if 0x20<=ord(c)<=0x7f : - if unipart!='' : - out+='&'+base64.b64encode(unipart.encode('utf-16-be')).decode('ascii').rstrip('=')+'-' - unipart='' - out+=c - else : unipart+=c - if unipart!='' : - out+='&'+base64.b64encode(unipart.encode('utf-16-be')).decode('ascii').rstrip('=')+'-' - return out + """Encode a string into RFC2060 aka IMAP UTF7""" + out = '' + enc = '' + for c in s.replace('&','&-') + 'X': + if '\x20' <= c <= '\x7f': + if enc: + out += f'&{binascii.b2a_base64(enc.encode("utf-16-be")).rstrip(b"\n=").replace(b"/", b",").decode("ascii")}-' + enc = '' + out += c + else: + enc += c + return out[:-1] def escape_rc_string(arg): return "".join("\\x%2x" % ord(char) for char in arg)