mirror of
https://github.com/chatmail/relay.git
synced 2026-05-16 13:38:59 +00:00
Compare commits
4 Commits
link2xt/do
...
link2xt/si
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9456be5bc | ||
|
|
f5bfa6bd56 | ||
|
|
81a6f8808b | ||
|
|
be3685519f |
@@ -151,10 +151,12 @@ While this file is present, account creation will be blocked.
|
|||||||
|
|
||||||
### Ports
|
### Ports
|
||||||
|
|
||||||
Postfix listens on ports 25 (smtp) and 587 (submission) and 465 (submissions).
|
[Postfix](http://www.postfix.org/) listens on ports 25 (smtp) and 587 (submission) and 465 (submissions).
|
||||||
Dovecot listens on ports 143(imap) and 993 (imaps).
|
[Dovecot](https://www.dovecot.org/) listens on ports 143 (imap) and 993 (imaps).
|
||||||
|
[nginx](https://www.nginx.com/) listens on port 443 (https).
|
||||||
|
[acmetool](https://hlandau.github.io/acmetool/) listens on port 80 (http).
|
||||||
|
|
||||||
Delta Chat apps will, however, discover all ports and configurations
|
Delta Chat apps will, however, discover all ports and configurations
|
||||||
automatically by reading the `autoconfig.xml` file from the chatmail service.
|
automatically by reading the [autoconfig XML file](https://web.archive.org/web/20210624004729/https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration) from the chatmail service.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -98,6 +98,32 @@ def lookup_passdb(db, config: Config, user, cleartext_password):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def split_and_unescape(s):
|
||||||
|
"""Split strings using double quote as a separator and backslash as escape character
|
||||||
|
into parts."""
|
||||||
|
|
||||||
|
out = ""
|
||||||
|
i = 0
|
||||||
|
while i < len(s):
|
||||||
|
c = s[i]
|
||||||
|
if c == "\\":
|
||||||
|
# Skip escape character.
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
# This will raise IndexError if there is no character
|
||||||
|
# after escape character. This is expected
|
||||||
|
# as this is an invalid input.
|
||||||
|
out += s[i]
|
||||||
|
elif c == '"':
|
||||||
|
# Separator
|
||||||
|
yield out
|
||||||
|
out = ""
|
||||||
|
else:
|
||||||
|
out += c
|
||||||
|
i += 1
|
||||||
|
yield out
|
||||||
|
|
||||||
|
|
||||||
def handle_dovecot_request(msg, db, config: Config):
|
def handle_dovecot_request(msg, db, config: Config):
|
||||||
short_command = msg[0]
|
short_command = msg[0]
|
||||||
if short_command == "L": # LOOKUP
|
if short_command == "L": # LOOKUP
|
||||||
@@ -107,7 +133,9 @@ def handle_dovecot_request(msg, db, config: Config):
|
|||||||
# do not attempt to read any other parts for compatibility.
|
# do not attempt to read any other parts for compatibility.
|
||||||
keyname = parts[0]
|
keyname = parts[0]
|
||||||
|
|
||||||
namespace, type, *args = keyname.split("/")
|
namespace, type, args = keyname.split("/", 2)
|
||||||
|
args = list(split_and_unescape(args))
|
||||||
|
|
||||||
reply_command = "F"
|
reply_command = "F"
|
||||||
res = ""
|
res = ""
|
||||||
if namespace == "shared":
|
if namespace == "shared":
|
||||||
|
|||||||
@@ -52,8 +52,9 @@ def test_too_high_db_version(db):
|
|||||||
|
|
||||||
|
|
||||||
def test_handle_dovecot_request(db, example_config):
|
def test_handle_dovecot_request(db, example_config):
|
||||||
|
# Test that password can contain ", ', \ and /
|
||||||
msg = (
|
msg = (
|
||||||
"Lshared/passdb/laksjdlaksjdlaksjdlk12j3l1k2j3123/"
|
'Lshared/passdb/laksjdlaksjdlak\\\\sjdlk\\"12j\\\'3l1/k2j3123"'
|
||||||
"some42123@chat.example.org\tsome42123@chat.example.org"
|
"some42123@chat.example.org\tsome42123@chat.example.org"
|
||||||
)
|
)
|
||||||
res = handle_dovecot_request(msg, db, example_config)
|
res = handle_dovecot_request(msg, db, example_config)
|
||||||
|
|||||||
@@ -37,21 +37,15 @@ class DNS:
|
|||||||
|
|
||||||
def get(self, typ: str, domain: str) -> str | None:
|
def get(self, typ: str, domain: str) -> str | None:
|
||||||
"""Get a DNS entry"""
|
"""Get a DNS entry"""
|
||||||
dig_result = self.shell(f"dig {typ} {domain}")
|
dig_result = self.shell(f"dig -r -q {domain} -t {typ} +short")
|
||||||
line_num = 0
|
line = dig_result.partition("\n")[0]
|
||||||
for line in dig_result.splitlines():
|
if line:
|
||||||
line_num += 1
|
return line
|
||||||
if line.strip() == ";; ANSWER SECTION:":
|
|
||||||
return dig_result.splitlines()[line_num].split("\t")[-1]
|
|
||||||
|
|
||||||
def check_ptr_record(self, ip: str, mail_domain) -> str:
|
def check_ptr_record(self, ip: str, mail_domain) -> bool:
|
||||||
"""Check the PTR record for an IPv4 or IPv6 address."""
|
"""Check the PTR record for an IPv4 or IPv6 address."""
|
||||||
result = self.get("-x", ip)
|
result = self.shell(f"dig -r -x {ip} +short").rstrip()
|
||||||
if result:
|
return result == f"{mail_domain}."
|
||||||
if ip_address(ip).version == 6:
|
|
||||||
result = result.split()[-1]
|
|
||||||
if result[:-1] == mail_domain:
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def show_dns(args, out):
|
def show_dns(args, out):
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
uri = proxy:/run/dovecot/doveauth.socket:auth
|
uri = proxy:/run/dovecot/doveauth.socket:auth
|
||||||
iterate_disable = yes
|
iterate_disable = yes
|
||||||
default_pass_scheme = plain
|
default_pass_scheme = plain
|
||||||
password_key = passdb/%w/%u
|
# %E escapes characters " (double quote), ' (single quote) and \ (backslash) with \ (backslash).
|
||||||
user_key = userdb/%u
|
# See <https://doc.dovecot.org/configuration_manual/config_file/config_variables/#modifiers>
|
||||||
|
# for documentation.
|
||||||
|
#
|
||||||
|
# We escape user-provided input and use double quote as a separator.
|
||||||
|
password_key = passdb/%Ew"%Eu
|
||||||
|
user_key = userdb/%Eu
|
||||||
|
|||||||
@@ -14,3 +14,12 @@ def test_fastcgi_working(maildomain, chatmail_config):
|
|||||||
res = requests.post(url)
|
res = requests.post(url)
|
||||||
assert maildomain in res.json().get("email")
|
assert maildomain in res.json().get("email")
|
||||||
assert len(res.json().get("password")) > chatmail_config.password_min_length
|
assert len(res.json().get("password")) > chatmail_config.password_min_length
|
||||||
|
|
||||||
|
|
||||||
|
def test_newemail_configure(maildomain, rpc):
|
||||||
|
"""Test configuring accounts by scanning a QR code works."""
|
||||||
|
url = f"DCACCOUNT:https://{maildomain}/cgi-bin/newemail.py"
|
||||||
|
for i in range(3):
|
||||||
|
account_id = rpc.add_account()
|
||||||
|
rpc.set_config_from_qr(account_id, url)
|
||||||
|
rpc.configure(account_id)
|
||||||
|
|||||||
Reference in New Issue
Block a user