From 8fba5e831b045d9cf3cde157469054c29aa8bd7a Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 21 Oct 2026 13:59:58 +0200 Subject: [PATCH 1/2] Capture what automx answers today, before a Go program replaces it (issue 1630) --- .../01-thunderbird-autoconfig-ok.body | 1 + .../01-thunderbird-autoconfig-ok.headers | 3 ++ .../01-thunderbird-autoconfig-ok.request | 3 ++ .../01-thunderbird-autoconfig-ok.status | 1 + .../02-thunderbird-on-automx-name.body | 1 + .../02-thunderbird-on-automx-name.headers | 3 ++ .../02-thunderbird-on-automx-name.request | 2 ++ .../02-thunderbird-on-automx-name.status | 1 + .../03-thunderbird-on-autodiscover-name.body | 1 + ...3-thunderbird-on-autodiscover-name.headers | 3 ++ ...3-thunderbird-on-autodiscover-name.request | 2 ++ ...03-thunderbird-on-autodiscover-name.status | 1 + .../04-thunderbird-unknown-domain.body | 0 .../04-thunderbird-unknown-domain.headers | 2 ++ .../04-thunderbird-unknown-domain.request | 2 ++ .../04-thunderbird-unknown-domain.status | 1 + .../05-thunderbird-missing-emailaddress.body | 1 + ...5-thunderbird-missing-emailaddress.headers | 3 ++ ...5-thunderbird-missing-emailaddress.request | 2 ++ ...05-thunderbird-missing-emailaddress.status | 1 + .../testdata/captured/06-autoconfig-root.body | 1 + .../captured/06-autoconfig-root.headers | 3 ++ .../captured/06-autoconfig-root.request | 2 ++ .../captured/06-autoconfig-root.status | 1 + .../07-wellknown-autoconfig-path.body | 5 ++++ .../07-wellknown-autoconfig-path.headers | 3 ++ .../07-wellknown-autoconfig-path.request | 1 + .../07-wellknown-autoconfig-path.status | 1 + .../08-outlook-post-known-domain.body | 5 ++++ .../08-outlook-post-known-domain.headers | 3 ++ .../08-outlook-post-known-domain.request | 12 ++++++++ .../08-outlook-post-known-domain.status | 1 + .../09-outlook-post-unknown-domain.body | 0 .../09-outlook-post-unknown-domain.headers | 2 ++ .../09-outlook-post-unknown-domain.request | 6 ++++ .../09-outlook-post-unknown-domain.status | 1 + .../10-outlook-post-malformed-body.body | 5 ++++ .../10-outlook-post-malformed-body.headers | 3 ++ .../10-outlook-post-malformed-body.request | 5 ++++ .../10-outlook-post-malformed-body.status | 1 + .../11-outlook-post-missing-emailaddress.body | 1 + ...-outlook-post-missing-emailaddress.headers | 3 ++ ...-outlook-post-missing-emailaddress.request | 6 ++++ ...1-outlook-post-missing-emailaddress.status | 1 + .../captured/12-outlook-get-not-allowed.body | 5 ++++ .../12-outlook-get-not-allowed.headers | 4 +++ .../12-outlook-get-not-allowed.request | 2 ++ .../12-outlook-get-not-allowed.status | 1 + .../13-outlook-post-capitalised-path.body | 5 ++++ .../13-outlook-post-capitalised-path.headers | 3 ++ .../13-outlook-post-capitalised-path.request | 6 ++++ .../13-outlook-post-capitalised-path.status | 1 + .../captured/14-mobileconfig-no-slash.body | 5 ++++ .../captured/14-mobileconfig-no-slash.headers | 4 +++ .../captured/14-mobileconfig-no-slash.request | 3 ++ .../captured/14-mobileconfig-no-slash.status | 1 + .../15-mobileconfig-known-domain.body | 5 ++++ .../15-mobileconfig-known-domain.headers | 3 ++ .../15-mobileconfig-known-domain.request | 3 ++ .../15-mobileconfig-known-domain.status | 1 + .../16-mobileconfig-unknown-domain.body | 0 .../16-mobileconfig-unknown-domain.headers | 2 ++ .../16-mobileconfig-unknown-domain.request | 2 ++ .../16-mobileconfig-unknown-domain.status | 1 + .../17-mobileconfig-missing-emailaddress.body | 1 + ...-mobileconfig-missing-emailaddress.headers | 3 ++ ...-mobileconfig-missing-emailaddress.request | 2 ++ ...7-mobileconfig-missing-emailaddress.status | 1 + .../cmd/automx/testdata/captured/CAPTURED | 30 +++++++++++++++++++ 69 files changed, 200 insertions(+) create mode 100644 modules/mailu/cmd/automx/testdata/captured/01-thunderbird-autoconfig-ok.body create mode 100644 modules/mailu/cmd/automx/testdata/captured/01-thunderbird-autoconfig-ok.headers create mode 100644 modules/mailu/cmd/automx/testdata/captured/01-thunderbird-autoconfig-ok.request create mode 100644 modules/mailu/cmd/automx/testdata/captured/01-thunderbird-autoconfig-ok.status create mode 100644 modules/mailu/cmd/automx/testdata/captured/02-thunderbird-on-automx-name.body create mode 100644 modules/mailu/cmd/automx/testdata/captured/02-thunderbird-on-automx-name.headers create mode 100644 modules/mailu/cmd/automx/testdata/captured/02-thunderbird-on-automx-name.request create mode 100644 modules/mailu/cmd/automx/testdata/captured/02-thunderbird-on-automx-name.status create mode 100644 modules/mailu/cmd/automx/testdata/captured/03-thunderbird-on-autodiscover-name.body create mode 100644 modules/mailu/cmd/automx/testdata/captured/03-thunderbird-on-autodiscover-name.headers create mode 100644 modules/mailu/cmd/automx/testdata/captured/03-thunderbird-on-autodiscover-name.request create mode 100644 modules/mailu/cmd/automx/testdata/captured/03-thunderbird-on-autodiscover-name.status create mode 100644 modules/mailu/cmd/automx/testdata/captured/04-thunderbird-unknown-domain.body create mode 100644 modules/mailu/cmd/automx/testdata/captured/04-thunderbird-unknown-domain.headers create mode 100644 modules/mailu/cmd/automx/testdata/captured/04-thunderbird-unknown-domain.request create mode 100644 modules/mailu/cmd/automx/testdata/captured/04-thunderbird-unknown-domain.status create mode 100644 modules/mailu/cmd/automx/testdata/captured/05-thunderbird-missing-emailaddress.body create mode 100644 modules/mailu/cmd/automx/testdata/captured/05-thunderbird-missing-emailaddress.headers create mode 100644 modules/mailu/cmd/automx/testdata/captured/05-thunderbird-missing-emailaddress.request create mode 100644 modules/mailu/cmd/automx/testdata/captured/05-thunderbird-missing-emailaddress.status create mode 100644 modules/mailu/cmd/automx/testdata/captured/06-autoconfig-root.body create mode 100644 modules/mailu/cmd/automx/testdata/captured/06-autoconfig-root.headers create mode 100644 modules/mailu/cmd/automx/testdata/captured/06-autoconfig-root.request create mode 100644 modules/mailu/cmd/automx/testdata/captured/06-autoconfig-root.status create mode 100644 modules/mailu/cmd/automx/testdata/captured/07-wellknown-autoconfig-path.body create mode 100644 modules/mailu/cmd/automx/testdata/captured/07-wellknown-autoconfig-path.headers create mode 100644 modules/mailu/cmd/automx/testdata/captured/07-wellknown-autoconfig-path.request create mode 100644 modules/mailu/cmd/automx/testdata/captured/07-wellknown-autoconfig-path.status create mode 100644 modules/mailu/cmd/automx/testdata/captured/08-outlook-post-known-domain.body create mode 100644 modules/mailu/cmd/automx/testdata/captured/08-outlook-post-known-domain.headers create mode 100644 modules/mailu/cmd/automx/testdata/captured/08-outlook-post-known-domain.request create mode 100644 modules/mailu/cmd/automx/testdata/captured/08-outlook-post-known-domain.status create mode 100644 modules/mailu/cmd/automx/testdata/captured/09-outlook-post-unknown-domain.body create mode 100644 modules/mailu/cmd/automx/testdata/captured/09-outlook-post-unknown-domain.headers create mode 100644 modules/mailu/cmd/automx/testdata/captured/09-outlook-post-unknown-domain.request create mode 100644 modules/mailu/cmd/automx/testdata/captured/09-outlook-post-unknown-domain.status create mode 100644 modules/mailu/cmd/automx/testdata/captured/10-outlook-post-malformed-body.body create mode 100644 modules/mailu/cmd/automx/testdata/captured/10-outlook-post-malformed-body.headers create mode 100644 modules/mailu/cmd/automx/testdata/captured/10-outlook-post-malformed-body.request create mode 100644 modules/mailu/cmd/automx/testdata/captured/10-outlook-post-malformed-body.status create mode 100644 modules/mailu/cmd/automx/testdata/captured/11-outlook-post-missing-emailaddress.body create mode 100644 modules/mailu/cmd/automx/testdata/captured/11-outlook-post-missing-emailaddress.headers create mode 100644 modules/mailu/cmd/automx/testdata/captured/11-outlook-post-missing-emailaddress.request create mode 100644 modules/mailu/cmd/automx/testdata/captured/11-outlook-post-missing-emailaddress.status create mode 100644 modules/mailu/cmd/automx/testdata/captured/12-outlook-get-not-allowed.body create mode 100644 modules/mailu/cmd/automx/testdata/captured/12-outlook-get-not-allowed.headers create mode 100644 modules/mailu/cmd/automx/testdata/captured/12-outlook-get-not-allowed.request create mode 100644 modules/mailu/cmd/automx/testdata/captured/12-outlook-get-not-allowed.status create mode 100644 modules/mailu/cmd/automx/testdata/captured/13-outlook-post-capitalised-path.body create mode 100644 modules/mailu/cmd/automx/testdata/captured/13-outlook-post-capitalised-path.headers create mode 100644 modules/mailu/cmd/automx/testdata/captured/13-outlook-post-capitalised-path.request create mode 100644 modules/mailu/cmd/automx/testdata/captured/13-outlook-post-capitalised-path.status create mode 100644 modules/mailu/cmd/automx/testdata/captured/14-mobileconfig-no-slash.body create mode 100644 modules/mailu/cmd/automx/testdata/captured/14-mobileconfig-no-slash.headers create mode 100644 modules/mailu/cmd/automx/testdata/captured/14-mobileconfig-no-slash.request create mode 100644 modules/mailu/cmd/automx/testdata/captured/14-mobileconfig-no-slash.status create mode 100644 modules/mailu/cmd/automx/testdata/captured/15-mobileconfig-known-domain.body create mode 100644 modules/mailu/cmd/automx/testdata/captured/15-mobileconfig-known-domain.headers create mode 100644 modules/mailu/cmd/automx/testdata/captured/15-mobileconfig-known-domain.request create mode 100644 modules/mailu/cmd/automx/testdata/captured/15-mobileconfig-known-domain.status create mode 100644 modules/mailu/cmd/automx/testdata/captured/16-mobileconfig-unknown-domain.body create mode 100644 modules/mailu/cmd/automx/testdata/captured/16-mobileconfig-unknown-domain.headers create mode 100644 modules/mailu/cmd/automx/testdata/captured/16-mobileconfig-unknown-domain.request create mode 100644 modules/mailu/cmd/automx/testdata/captured/16-mobileconfig-unknown-domain.status create mode 100644 modules/mailu/cmd/automx/testdata/captured/17-mobileconfig-missing-emailaddress.body create mode 100644 modules/mailu/cmd/automx/testdata/captured/17-mobileconfig-missing-emailaddress.headers create mode 100644 modules/mailu/cmd/automx/testdata/captured/17-mobileconfig-missing-emailaddress.request create mode 100644 modules/mailu/cmd/automx/testdata/captured/17-mobileconfig-missing-emailaddress.status create mode 100644 modules/mailu/cmd/automx/testdata/captured/CAPTURED diff --git a/modules/mailu/cmd/automx/testdata/captured/01-thunderbird-autoconfig-ok.body b/modules/mailu/cmd/automx/testdata/captured/01-thunderbird-autoconfig-ok.body new file mode 100644 index 00000000..72d5c376 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/01-thunderbird-autoconfig-ok.body @@ -0,0 +1 @@ +novox.benovox.benovox.be143STARTTLS%EMAILADDRESS%password-cleartextmail.novox.be587STARTTLS%EMAILADDRESS%password-cleartext \ No newline at end of file diff --git a/modules/mailu/cmd/automx/testdata/captured/01-thunderbird-autoconfig-ok.headers b/modules/mailu/cmd/automx/testdata/captured/01-thunderbird-autoconfig-ok.headers new file mode 100644 index 00000000..01eb3d79 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/01-thunderbird-autoconfig-ok.headers @@ -0,0 +1,3 @@ +Content-Length: 612 +Content-Type: application/xml +Server: Werkzeug/3.1.9 Python/3.9.5 diff --git a/modules/mailu/cmd/automx/testdata/captured/01-thunderbird-autoconfig-ok.request b/modules/mailu/cmd/automx/testdata/captured/01-thunderbird-autoconfig-ok.request new file mode 100644 index 00000000..98ae52c5 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/01-thunderbird-autoconfig-ok.request @@ -0,0 +1,3 @@ +GET https://autoconfig.novox.be/mail/config-v1.1.xml?emailaddress=jochen@novox.be +User-Agent: Thunderbird +Accept: */* diff --git a/modules/mailu/cmd/automx/testdata/captured/01-thunderbird-autoconfig-ok.status b/modules/mailu/cmd/automx/testdata/captured/01-thunderbird-autoconfig-ok.status new file mode 100644 index 00000000..992a7e84 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/01-thunderbird-autoconfig-ok.status @@ -0,0 +1 @@ +200 OK diff --git a/modules/mailu/cmd/automx/testdata/captured/02-thunderbird-on-automx-name.body b/modules/mailu/cmd/automx/testdata/captured/02-thunderbird-on-automx-name.body new file mode 100644 index 00000000..72d5c376 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/02-thunderbird-on-automx-name.body @@ -0,0 +1 @@ +novox.benovox.benovox.be143STARTTLS%EMAILADDRESS%password-cleartextmail.novox.be587STARTTLS%EMAILADDRESS%password-cleartext \ No newline at end of file diff --git a/modules/mailu/cmd/automx/testdata/captured/02-thunderbird-on-automx-name.headers b/modules/mailu/cmd/automx/testdata/captured/02-thunderbird-on-automx-name.headers new file mode 100644 index 00000000..01eb3d79 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/02-thunderbird-on-automx-name.headers @@ -0,0 +1,3 @@ +Content-Length: 612 +Content-Type: application/xml +Server: Werkzeug/3.1.9 Python/3.9.5 diff --git a/modules/mailu/cmd/automx/testdata/captured/02-thunderbird-on-automx-name.request b/modules/mailu/cmd/automx/testdata/captured/02-thunderbird-on-automx-name.request new file mode 100644 index 00000000..2a6405cf --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/02-thunderbird-on-automx-name.request @@ -0,0 +1,2 @@ +GET https://automx.novox.be/mail/config-v1.1.xml?emailaddress=jochen@novox.be +User-Agent: Thunderbird diff --git a/modules/mailu/cmd/automx/testdata/captured/02-thunderbird-on-automx-name.status b/modules/mailu/cmd/automx/testdata/captured/02-thunderbird-on-automx-name.status new file mode 100644 index 00000000..992a7e84 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/02-thunderbird-on-automx-name.status @@ -0,0 +1 @@ +200 OK diff --git a/modules/mailu/cmd/automx/testdata/captured/03-thunderbird-on-autodiscover-name.body b/modules/mailu/cmd/automx/testdata/captured/03-thunderbird-on-autodiscover-name.body new file mode 100644 index 00000000..72d5c376 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/03-thunderbird-on-autodiscover-name.body @@ -0,0 +1 @@ +novox.benovox.benovox.be143STARTTLS%EMAILADDRESS%password-cleartextmail.novox.be587STARTTLS%EMAILADDRESS%password-cleartext \ No newline at end of file diff --git a/modules/mailu/cmd/automx/testdata/captured/03-thunderbird-on-autodiscover-name.headers b/modules/mailu/cmd/automx/testdata/captured/03-thunderbird-on-autodiscover-name.headers new file mode 100644 index 00000000..01eb3d79 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/03-thunderbird-on-autodiscover-name.headers @@ -0,0 +1,3 @@ +Content-Length: 612 +Content-Type: application/xml +Server: Werkzeug/3.1.9 Python/3.9.5 diff --git a/modules/mailu/cmd/automx/testdata/captured/03-thunderbird-on-autodiscover-name.request b/modules/mailu/cmd/automx/testdata/captured/03-thunderbird-on-autodiscover-name.request new file mode 100644 index 00000000..5b33fcce --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/03-thunderbird-on-autodiscover-name.request @@ -0,0 +1,2 @@ +GET https://autodiscover.novox.be/mail/config-v1.1.xml?emailaddress=jochen@novox.be +User-Agent: Thunderbird diff --git a/modules/mailu/cmd/automx/testdata/captured/03-thunderbird-on-autodiscover-name.status b/modules/mailu/cmd/automx/testdata/captured/03-thunderbird-on-autodiscover-name.status new file mode 100644 index 00000000..992a7e84 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/03-thunderbird-on-autodiscover-name.status @@ -0,0 +1 @@ +200 OK diff --git a/modules/mailu/cmd/automx/testdata/captured/04-thunderbird-unknown-domain.body b/modules/mailu/cmd/automx/testdata/captured/04-thunderbird-unknown-domain.body new file mode 100644 index 00000000..e69de29b diff --git a/modules/mailu/cmd/automx/testdata/captured/04-thunderbird-unknown-domain.headers b/modules/mailu/cmd/automx/testdata/captured/04-thunderbird-unknown-domain.headers new file mode 100644 index 00000000..85abd033 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/04-thunderbird-unknown-domain.headers @@ -0,0 +1,2 @@ +Content-Type: text/html; charset=utf-8 +Server: Werkzeug/3.1.9 Python/3.9.5 diff --git a/modules/mailu/cmd/automx/testdata/captured/04-thunderbird-unknown-domain.request b/modules/mailu/cmd/automx/testdata/captured/04-thunderbird-unknown-domain.request new file mode 100644 index 00000000..d2459a88 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/04-thunderbird-unknown-domain.request @@ -0,0 +1,2 @@ +GET https://autoconfig.novox.be/mail/config-v1.1.xml?emailaddress=nobody@unknown.example +User-Agent: Thunderbird diff --git a/modules/mailu/cmd/automx/testdata/captured/04-thunderbird-unknown-domain.status b/modules/mailu/cmd/automx/testdata/captured/04-thunderbird-unknown-domain.status new file mode 100644 index 00000000..bd3152a3 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/04-thunderbird-unknown-domain.status @@ -0,0 +1 @@ +204 No Content diff --git a/modules/mailu/cmd/automx/testdata/captured/05-thunderbird-missing-emailaddress.body b/modules/mailu/cmd/automx/testdata/captured/05-thunderbird-missing-emailaddress.body new file mode 100644 index 00000000..7f0ad25f --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/05-thunderbird-missing-emailaddress.body @@ -0,0 +1 @@ +Missing request argument "emailaddress" \ No newline at end of file diff --git a/modules/mailu/cmd/automx/testdata/captured/05-thunderbird-missing-emailaddress.headers b/modules/mailu/cmd/automx/testdata/captured/05-thunderbird-missing-emailaddress.headers new file mode 100644 index 00000000..64102801 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/05-thunderbird-missing-emailaddress.headers @@ -0,0 +1,3 @@ +Content-Length: 39 +Content-Type: text/html; charset=utf-8 +Server: Werkzeug/3.1.9 Python/3.9.5 diff --git a/modules/mailu/cmd/automx/testdata/captured/05-thunderbird-missing-emailaddress.request b/modules/mailu/cmd/automx/testdata/captured/05-thunderbird-missing-emailaddress.request new file mode 100644 index 00000000..5d521f35 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/05-thunderbird-missing-emailaddress.request @@ -0,0 +1,2 @@ +GET https://autoconfig.novox.be/mail/config-v1.1.xml +User-Agent: Thunderbird diff --git a/modules/mailu/cmd/automx/testdata/captured/05-thunderbird-missing-emailaddress.status b/modules/mailu/cmd/automx/testdata/captured/05-thunderbird-missing-emailaddress.status new file mode 100644 index 00000000..59db10c4 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/05-thunderbird-missing-emailaddress.status @@ -0,0 +1 @@ +400 Bad Request diff --git a/modules/mailu/cmd/automx/testdata/captured/06-autoconfig-root.body b/modules/mailu/cmd/automx/testdata/captured/06-autoconfig-root.body new file mode 100644 index 00000000..8c25486a --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/06-autoconfig-root.body @@ -0,0 +1 @@ +Show Thunderbird-style XML configuration for abc@example.com. \ No newline at end of file diff --git a/modules/mailu/cmd/automx/testdata/captured/06-autoconfig-root.headers b/modules/mailu/cmd/automx/testdata/captured/06-autoconfig-root.headers new file mode 100644 index 00000000..5cd620c0 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/06-autoconfig-root.headers @@ -0,0 +1,3 @@ +Content-Length: 152 +Content-Type: text/html; charset=utf-8 +Server: Werkzeug/3.1.9 Python/3.9.5 diff --git a/modules/mailu/cmd/automx/testdata/captured/06-autoconfig-root.request b/modules/mailu/cmd/automx/testdata/captured/06-autoconfig-root.request new file mode 100644 index 00000000..4dc45b54 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/06-autoconfig-root.request @@ -0,0 +1,2 @@ +GET https://autoconfig.novox.be/ +User-Agent: Mozilla/5.0 diff --git a/modules/mailu/cmd/automx/testdata/captured/06-autoconfig-root.status b/modules/mailu/cmd/automx/testdata/captured/06-autoconfig-root.status new file mode 100644 index 00000000..992a7e84 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/06-autoconfig-root.status @@ -0,0 +1 @@ +200 OK diff --git a/modules/mailu/cmd/automx/testdata/captured/07-wellknown-autoconfig-path.body b/modules/mailu/cmd/automx/testdata/captured/07-wellknown-autoconfig-path.body new file mode 100644 index 00000000..affacdf5 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/07-wellknown-autoconfig-path.body @@ -0,0 +1,5 @@ + + +404 Not Found +

Not Found

+

The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.

diff --git a/modules/mailu/cmd/automx/testdata/captured/07-wellknown-autoconfig-path.headers b/modules/mailu/cmd/automx/testdata/captured/07-wellknown-autoconfig-path.headers new file mode 100644 index 00000000..11f1fdb6 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/07-wellknown-autoconfig-path.headers @@ -0,0 +1,3 @@ +Content-Length: 207 +Content-Type: text/html; charset=utf-8 +Server: Werkzeug/3.1.9 Python/3.9.5 diff --git a/modules/mailu/cmd/automx/testdata/captured/07-wellknown-autoconfig-path.request b/modules/mailu/cmd/automx/testdata/captured/07-wellknown-autoconfig-path.request new file mode 100644 index 00000000..d45d9935 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/07-wellknown-autoconfig-path.request @@ -0,0 +1 @@ +GET https://autoconfig.novox.be/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress=jochen@novox.be diff --git a/modules/mailu/cmd/automx/testdata/captured/07-wellknown-autoconfig-path.status b/modules/mailu/cmd/automx/testdata/captured/07-wellknown-autoconfig-path.status new file mode 100644 index 00000000..44d986c4 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/07-wellknown-autoconfig-path.status @@ -0,0 +1 @@ +404 Not Found diff --git a/modules/mailu/cmd/automx/testdata/captured/08-outlook-post-known-domain.body b/modules/mailu/cmd/automx/testdata/captured/08-outlook-post-known-domain.body new file mode 100644 index 00000000..a38132d8 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/08-outlook-post-known-domain.body @@ -0,0 +1,5 @@ + + +500 Internal Server Error +

Internal Server Error

+

The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.

diff --git a/modules/mailu/cmd/automx/testdata/captured/08-outlook-post-known-domain.headers b/modules/mailu/cmd/automx/testdata/captured/08-outlook-post-known-domain.headers new file mode 100644 index 00000000..463e2827 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/08-outlook-post-known-domain.headers @@ -0,0 +1,3 @@ +Content-Length: 265 +Content-Type: text/html; charset=utf-8 +Server: Werkzeug/3.1.9 Python/3.9.5 diff --git a/modules/mailu/cmd/automx/testdata/captured/08-outlook-post-known-domain.request b/modules/mailu/cmd/automx/testdata/captured/08-outlook-post-known-domain.request new file mode 100644 index 00000000..258c92a6 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/08-outlook-post-known-domain.request @@ -0,0 +1,12 @@ +POST https://autodiscover.novox.be/autodiscover/autodiscover.xml +User-Agent: Microsoft Office/16.0 (Windows NT 10.0; Microsoft Outlook 16.0.14326; Pro) +Content-Type: text/xml +Accept: */* + + + + + jochen@novox.be + http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a + + diff --git a/modules/mailu/cmd/automx/testdata/captured/08-outlook-post-known-domain.status b/modules/mailu/cmd/automx/testdata/captured/08-outlook-post-known-domain.status new file mode 100644 index 00000000..e534a491 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/08-outlook-post-known-domain.status @@ -0,0 +1 @@ +500 Internal Server Error diff --git a/modules/mailu/cmd/automx/testdata/captured/09-outlook-post-unknown-domain.body b/modules/mailu/cmd/automx/testdata/captured/09-outlook-post-unknown-domain.body new file mode 100644 index 00000000..e69de29b diff --git a/modules/mailu/cmd/automx/testdata/captured/09-outlook-post-unknown-domain.headers b/modules/mailu/cmd/automx/testdata/captured/09-outlook-post-unknown-domain.headers new file mode 100644 index 00000000..85abd033 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/09-outlook-post-unknown-domain.headers @@ -0,0 +1,2 @@ +Content-Type: text/html; charset=utf-8 +Server: Werkzeug/3.1.9 Python/3.9.5 diff --git a/modules/mailu/cmd/automx/testdata/captured/09-outlook-post-unknown-domain.request b/modules/mailu/cmd/automx/testdata/captured/09-outlook-post-unknown-domain.request new file mode 100644 index 00000000..990a009c --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/09-outlook-post-unknown-domain.request @@ -0,0 +1,6 @@ +POST https://autodiscover.novox.be/autodiscover/autodiscover.xml +User-Agent: Microsoft Office/16.0 +Content-Type: text/xml + + +nobody@unknown.examplehttp://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a diff --git a/modules/mailu/cmd/automx/testdata/captured/09-outlook-post-unknown-domain.status b/modules/mailu/cmd/automx/testdata/captured/09-outlook-post-unknown-domain.status new file mode 100644 index 00000000..bd3152a3 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/09-outlook-post-unknown-domain.status @@ -0,0 +1 @@ +204 No Content diff --git a/modules/mailu/cmd/automx/testdata/captured/10-outlook-post-malformed-body.body b/modules/mailu/cmd/automx/testdata/captured/10-outlook-post-malformed-body.body new file mode 100644 index 00000000..a38132d8 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/10-outlook-post-malformed-body.body @@ -0,0 +1,5 @@ + + +500 Internal Server Error +

Internal Server Error

+

The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.

diff --git a/modules/mailu/cmd/automx/testdata/captured/10-outlook-post-malformed-body.headers b/modules/mailu/cmd/automx/testdata/captured/10-outlook-post-malformed-body.headers new file mode 100644 index 00000000..463e2827 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/10-outlook-post-malformed-body.headers @@ -0,0 +1,3 @@ +Content-Length: 265 +Content-Type: text/html; charset=utf-8 +Server: Werkzeug/3.1.9 Python/3.9.5 diff --git a/modules/mailu/cmd/automx/testdata/captured/10-outlook-post-malformed-body.request b/modules/mailu/cmd/automx/testdata/captured/10-outlook-post-malformed-body.request new file mode 100644 index 00000000..7467eeee --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/10-outlook-post-malformed-body.request @@ -0,0 +1,5 @@ +POST https://autodiscover.novox.be/autodiscover/autodiscover.xml +User-Agent: Microsoft Office/16.0 +Content-Type: text/xml + +this is not xml \ No newline at end of file diff --git a/modules/mailu/cmd/automx/testdata/captured/10-outlook-post-malformed-body.status b/modules/mailu/cmd/automx/testdata/captured/10-outlook-post-malformed-body.status new file mode 100644 index 00000000..e534a491 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/10-outlook-post-malformed-body.status @@ -0,0 +1 @@ +500 Internal Server Error diff --git a/modules/mailu/cmd/automx/testdata/captured/11-outlook-post-missing-emailaddress.body b/modules/mailu/cmd/automx/testdata/captured/11-outlook-post-missing-emailaddress.body new file mode 100644 index 00000000..c52afca0 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/11-outlook-post-missing-emailaddress.body @@ -0,0 +1 @@ +Missing request argument "EMailAddress" \ No newline at end of file diff --git a/modules/mailu/cmd/automx/testdata/captured/11-outlook-post-missing-emailaddress.headers b/modules/mailu/cmd/automx/testdata/captured/11-outlook-post-missing-emailaddress.headers new file mode 100644 index 00000000..64102801 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/11-outlook-post-missing-emailaddress.headers @@ -0,0 +1,3 @@ +Content-Length: 39 +Content-Type: text/html; charset=utf-8 +Server: Werkzeug/3.1.9 Python/3.9.5 diff --git a/modules/mailu/cmd/automx/testdata/captured/11-outlook-post-missing-emailaddress.request b/modules/mailu/cmd/automx/testdata/captured/11-outlook-post-missing-emailaddress.request new file mode 100644 index 00000000..abea016e --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/11-outlook-post-missing-emailaddress.request @@ -0,0 +1,6 @@ +POST https://autodiscover.novox.be/autodiscover/autodiscover.xml +User-Agent: Microsoft Office/16.0 +Content-Type: text/xml + + +http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a diff --git a/modules/mailu/cmd/automx/testdata/captured/11-outlook-post-missing-emailaddress.status b/modules/mailu/cmd/automx/testdata/captured/11-outlook-post-missing-emailaddress.status new file mode 100644 index 00000000..59db10c4 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/11-outlook-post-missing-emailaddress.status @@ -0,0 +1 @@ +400 Bad Request diff --git a/modules/mailu/cmd/automx/testdata/captured/12-outlook-get-not-allowed.body b/modules/mailu/cmd/automx/testdata/captured/12-outlook-get-not-allowed.body new file mode 100644 index 00000000..08dacbdc --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/12-outlook-get-not-allowed.body @@ -0,0 +1,5 @@ + + +405 Method Not Allowed +

Method Not Allowed

+

The method is not allowed for the requested URL.

diff --git a/modules/mailu/cmd/automx/testdata/captured/12-outlook-get-not-allowed.headers b/modules/mailu/cmd/automx/testdata/captured/12-outlook-get-not-allowed.headers new file mode 100644 index 00000000..84e74dc7 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/12-outlook-get-not-allowed.headers @@ -0,0 +1,4 @@ +Allow: POST, OPTIONS +Content-Length: 153 +Content-Type: text/html; charset=utf-8 +Server: Werkzeug/3.1.9 Python/3.9.5 diff --git a/modules/mailu/cmd/automx/testdata/captured/12-outlook-get-not-allowed.request b/modules/mailu/cmd/automx/testdata/captured/12-outlook-get-not-allowed.request new file mode 100644 index 00000000..11530419 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/12-outlook-get-not-allowed.request @@ -0,0 +1,2 @@ +GET https://autodiscover.novox.be/autodiscover/autodiscover.xml +User-Agent: Microsoft Office/16.0 diff --git a/modules/mailu/cmd/automx/testdata/captured/12-outlook-get-not-allowed.status b/modules/mailu/cmd/automx/testdata/captured/12-outlook-get-not-allowed.status new file mode 100644 index 00000000..ddf33a47 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/12-outlook-get-not-allowed.status @@ -0,0 +1 @@ +405 Method Not Allowed diff --git a/modules/mailu/cmd/automx/testdata/captured/13-outlook-post-capitalised-path.body b/modules/mailu/cmd/automx/testdata/captured/13-outlook-post-capitalised-path.body new file mode 100644 index 00000000..affacdf5 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/13-outlook-post-capitalised-path.body @@ -0,0 +1,5 @@ + + +404 Not Found +

Not Found

+

The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.

diff --git a/modules/mailu/cmd/automx/testdata/captured/13-outlook-post-capitalised-path.headers b/modules/mailu/cmd/automx/testdata/captured/13-outlook-post-capitalised-path.headers new file mode 100644 index 00000000..11f1fdb6 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/13-outlook-post-capitalised-path.headers @@ -0,0 +1,3 @@ +Content-Length: 207 +Content-Type: text/html; charset=utf-8 +Server: Werkzeug/3.1.9 Python/3.9.5 diff --git a/modules/mailu/cmd/automx/testdata/captured/13-outlook-post-capitalised-path.request b/modules/mailu/cmd/automx/testdata/captured/13-outlook-post-capitalised-path.request new file mode 100644 index 00000000..ff129abe --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/13-outlook-post-capitalised-path.request @@ -0,0 +1,6 @@ +POST https://autodiscover.novox.be/Autodiscover/Autodiscover.xml +User-Agent: Microsoft Office/16.0 +Content-Type: text/xml + + +jochen@novox.behttp://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a diff --git a/modules/mailu/cmd/automx/testdata/captured/13-outlook-post-capitalised-path.status b/modules/mailu/cmd/automx/testdata/captured/13-outlook-post-capitalised-path.status new file mode 100644 index 00000000..44d986c4 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/13-outlook-post-capitalised-path.status @@ -0,0 +1 @@ +404 Not Found diff --git a/modules/mailu/cmd/automx/testdata/captured/14-mobileconfig-no-slash.body b/modules/mailu/cmd/automx/testdata/captured/14-mobileconfig-no-slash.body new file mode 100644 index 00000000..bc384fc9 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/14-mobileconfig-no-slash.body @@ -0,0 +1,5 @@ + + +Redirecting... +

Redirecting...

+

You should be redirected automatically to the target URL: https://automx.novox.be/mobileconfig/?emailaddress=jochen@novox.be. If not, click the link. diff --git a/modules/mailu/cmd/automx/testdata/captured/14-mobileconfig-no-slash.headers b/modules/mailu/cmd/automx/testdata/captured/14-mobileconfig-no-slash.headers new file mode 100644 index 00000000..75822817 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/14-mobileconfig-no-slash.headers @@ -0,0 +1,4 @@ +Content-Length: 319 +Content-Type: text/html; charset=utf-8 +Location: https://automx.novox.be/mobileconfig/?emailaddress=jochen@novox.be +Server: Werkzeug/3.1.9 Python/3.9.5 diff --git a/modules/mailu/cmd/automx/testdata/captured/14-mobileconfig-no-slash.request b/modules/mailu/cmd/automx/testdata/captured/14-mobileconfig-no-slash.request new file mode 100644 index 00000000..ee86c5ba --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/14-mobileconfig-no-slash.request @@ -0,0 +1,3 @@ +GET https://automx.novox.be/mobileconfig?emailaddress=jochen@novox.be +User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) Safari +Accept: */* diff --git a/modules/mailu/cmd/automx/testdata/captured/14-mobileconfig-no-slash.status b/modules/mailu/cmd/automx/testdata/captured/14-mobileconfig-no-slash.status new file mode 100644 index 00000000..8e8ce5e4 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/14-mobileconfig-no-slash.status @@ -0,0 +1 @@ +308 Permanent Redirect diff --git a/modules/mailu/cmd/automx/testdata/captured/15-mobileconfig-known-domain.body b/modules/mailu/cmd/automx/testdata/captured/15-mobileconfig-known-domain.body new file mode 100644 index 00000000..875543c0 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/15-mobileconfig-known-domain.body @@ -0,0 +1,5 @@ + + +400 Bad Request +

Bad Request

+

The browser (or proxy) sent a request that this server could not understand.

diff --git a/modules/mailu/cmd/automx/testdata/captured/15-mobileconfig-known-domain.headers b/modules/mailu/cmd/automx/testdata/captured/15-mobileconfig-known-domain.headers new file mode 100644 index 00000000..184e49a4 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/15-mobileconfig-known-domain.headers @@ -0,0 +1,3 @@ +Content-Length: 167 +Content-Type: text/html; charset=utf-8 +Server: Werkzeug/3.1.9 Python/3.9.5 diff --git a/modules/mailu/cmd/automx/testdata/captured/15-mobileconfig-known-domain.request b/modules/mailu/cmd/automx/testdata/captured/15-mobileconfig-known-domain.request new file mode 100644 index 00000000..47197a20 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/15-mobileconfig-known-domain.request @@ -0,0 +1,3 @@ +GET https://automx.novox.be/mobileconfig/?emailaddress=jochen@novox.be +User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) Safari +Accept: */* diff --git a/modules/mailu/cmd/automx/testdata/captured/15-mobileconfig-known-domain.status b/modules/mailu/cmd/automx/testdata/captured/15-mobileconfig-known-domain.status new file mode 100644 index 00000000..59db10c4 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/15-mobileconfig-known-domain.status @@ -0,0 +1 @@ +400 Bad Request diff --git a/modules/mailu/cmd/automx/testdata/captured/16-mobileconfig-unknown-domain.body b/modules/mailu/cmd/automx/testdata/captured/16-mobileconfig-unknown-domain.body new file mode 100644 index 00000000..e69de29b diff --git a/modules/mailu/cmd/automx/testdata/captured/16-mobileconfig-unknown-domain.headers b/modules/mailu/cmd/automx/testdata/captured/16-mobileconfig-unknown-domain.headers new file mode 100644 index 00000000..85abd033 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/16-mobileconfig-unknown-domain.headers @@ -0,0 +1,2 @@ +Content-Type: text/html; charset=utf-8 +Server: Werkzeug/3.1.9 Python/3.9.5 diff --git a/modules/mailu/cmd/automx/testdata/captured/16-mobileconfig-unknown-domain.request b/modules/mailu/cmd/automx/testdata/captured/16-mobileconfig-unknown-domain.request new file mode 100644 index 00000000..b38acb38 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/16-mobileconfig-unknown-domain.request @@ -0,0 +1,2 @@ +GET https://automx.novox.be/mobileconfig/?emailaddress=nobody@unknown.example +User-Agent: Mozilla/5.0 (iPhone) diff --git a/modules/mailu/cmd/automx/testdata/captured/16-mobileconfig-unknown-domain.status b/modules/mailu/cmd/automx/testdata/captured/16-mobileconfig-unknown-domain.status new file mode 100644 index 00000000..bd3152a3 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/16-mobileconfig-unknown-domain.status @@ -0,0 +1 @@ +204 No Content diff --git a/modules/mailu/cmd/automx/testdata/captured/17-mobileconfig-missing-emailaddress.body b/modules/mailu/cmd/automx/testdata/captured/17-mobileconfig-missing-emailaddress.body new file mode 100644 index 00000000..7f0ad25f --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/17-mobileconfig-missing-emailaddress.body @@ -0,0 +1 @@ +Missing request argument "emailaddress" \ No newline at end of file diff --git a/modules/mailu/cmd/automx/testdata/captured/17-mobileconfig-missing-emailaddress.headers b/modules/mailu/cmd/automx/testdata/captured/17-mobileconfig-missing-emailaddress.headers new file mode 100644 index 00000000..64102801 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/17-mobileconfig-missing-emailaddress.headers @@ -0,0 +1,3 @@ +Content-Length: 39 +Content-Type: text/html; charset=utf-8 +Server: Werkzeug/3.1.9 Python/3.9.5 diff --git a/modules/mailu/cmd/automx/testdata/captured/17-mobileconfig-missing-emailaddress.request b/modules/mailu/cmd/automx/testdata/captured/17-mobileconfig-missing-emailaddress.request new file mode 100644 index 00000000..a7f84362 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/17-mobileconfig-missing-emailaddress.request @@ -0,0 +1,2 @@ +GET https://automx.novox.be/mobileconfig/ +User-Agent: Mozilla/5.0 (iPhone) diff --git a/modules/mailu/cmd/automx/testdata/captured/17-mobileconfig-missing-emailaddress.status b/modules/mailu/cmd/automx/testdata/captured/17-mobileconfig-missing-emailaddress.status new file mode 100644 index 00000000..59db10c4 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/17-mobileconfig-missing-emailaddress.status @@ -0,0 +1 @@ +400 Bad Request diff --git a/modules/mailu/cmd/automx/testdata/captured/CAPTURED b/modules/mailu/cmd/automx/testdata/captured/CAPTURED new file mode 100644 index 00000000..88967524 --- /dev/null +++ b/modules/mailu/cmd/automx/testdata/captured/CAPTURED @@ -0,0 +1,30 @@ +What mailu's autoconfiguration module (automx2 2021.6, container mailu-automx on novox, Werkzeug/3.1.9 Python/3.9.5) +answers to the requests mail clients make (R4d step AMX-1, issue 1630, ADR 0480). + +Captured 2026-10-21 between 13:58:25 and 13:58:52 (CEST; the Date headers said 11:58 GMT) with the mesh tool +shanks/netcheck.netcheck_request, called from shanks, through the names clients use (autoconfig.novox.be, +autodiscover.novox.be, automx.novox.be: HTTPS, the mesh's route), no ssh. A redirect is reported, never followed. +Mail domain served: novox.be. Mailbox used: jochen@novox.be; unknown domain: nobody@unknown.example. + +Each capture is four files with the same stem: + .request method and URL, then the request headers sent, then (after a blank line) the body sent + .status the status line + .headers the response headers, sorted by name, without Date (it changes on every request) + .body the response body, byte for byte (Content-Length in .headers equals its size) +The files were written from the tool's answers by a script (not by retyping each by hand); the tool returns JSON, so +a later capture that wants exact bytes needs a tool that writes the answer to a file. + +What the answers show (read, not guessed): + - GET /mail/config-v1.1.xml?emailaddress=... answers 200 application/xml on all three names; same body on each. + - The incoming (IMAP) server has an EMPTY hostname (), port 143, STARTTLS. The outgoing server is + mail.novox.be:587 STARTTLS. Cause (from reading the module, not from the database): automx/files/add-domains:35 + reads AMX_IMAP_SERVER, files/mailu.env:58 sets AMX_IMAP_ADDRESS, so the IMAP name was never written. + A client given this answer cannot find its IMAP server. + - POST /autodiscover/autodiscover.xml with a valid request for the served domain answers 500 (the Outlook + answer for a real mailbox does not work today). Unknown domain: 204 no body. Malformed body: 500. + No EMailAddress element: 400 "Missing request argument "EMailAddress"". GET: 405 with Allow: POST, OPTIONS. + The path is case sensitive (/Autodiscover/Autodiscover.xml is 404). + - GET /mobileconfig?emailaddress=... redirects (308) to /mobileconfig/?emailaddress=...; for the served domain that + answers 400 with no profile; unknown domain 204; no emailaddress 400. + - GET / on autoconfig answers 200 with a small HTML page linking to an example address. + - Not captured: the database rows (no mesh tool reads /data/db.sqlite; issue 1630 AC4). From 454c5662ddcbd9c46a1308aa4123a6d65bdde3bf Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 21 Oct 2026 14:02:37 +0200 Subject: [PATCH 2/2] Add automx_rows, a read-only tool for the rows automx answers from (issue 1630) --- modules/mailu/automx.ts | 59 +++++++++++++++++++ modules/mailu/module.json | 3 +- modules/mailu/test/automx-rows.test.ts | 81 ++++++++++++++++++++++++++ modules/mailu/test/mail-flows.test.ts | 3 +- modules/mailu/tools/index.ts | 20 ++++++- modules/mailu/tsconfig.json | 2 +- 6 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 modules/mailu/automx.ts create mode 100644 modules/mailu/test/automx-rows.test.ts diff --git a/modules/mailu/automx.ts b/modules/mailu/automx.ts new file mode 100644 index 00000000..9af3b039 --- /dev/null +++ b/modules/mailu/automx.ts @@ -0,0 +1,59 @@ +// What the autoconfiguration module (automx2) holds, as stored: its provider, server, domain and server_domain +// rows, read from its SQLite file inside its container with the sqlite3 program the image carries, opened +// read-only. Nothing is written and nothing is interpreted: the rows are the evidence for what automx answers +// to mail clients (novox/hq issue 1630, ADR 0480), and the reason the answer may be empty. + +import type { Exec } from "./health.js"; + +export const AUTOMX_DATABASE = "/data/db.sqlite"; + +/** The tables read, each with the one query that reads it. Fixed text: the tool takes no query from its caller. */ +export const AUTOMX_TABLES: Record = { + provider: "SELECT id, name, short_name FROM provider ORDER BY id", + server: "SELECT id, prio, name, port, type, socket_type, user_name, authentication FROM server ORDER BY id", + domain: "SELECT id, name, provider_id, ldapserver_id FROM domain ORDER BY id", + server_domain: "SELECT server_id, domain_id FROM server_domain ORDER BY domain_id, server_id", +}; + +export type Row = Record; + +export type AutomxRows = { + database: string; + tables: Record; + /** servers whose name is empty: the client is told an empty host name for them */ + empty_names: { id: number; type: string }[]; + /** tables that could not be read, each with what sqlite3 or docker said on one line */ + unreadable: Record; +}; + +/** sqlite3 -json prints one JSON array, or nothing at all for a table without rows. */ +export function parseRows(output: string): Row[] { + const text = output.trim(); + if (text === "") return []; + const rows = JSON.parse(text) as unknown; + if (!Array.isArray(rows)) throw new Error("sqlite3 did not print a list of rows"); + return rows as Row[]; +} + +/** The command that reads one table: read-only, JSON, one fixed statement. */ +export function readCommand(sql: string): string[] { + return ["sqlite3", "-readonly", "-json", AUTOMX_DATABASE, sql]; +} + +/** Read every table; one that fails is said in `unreadable` and does not hide the others. */ +export async function readAutomx(exec: Exec, container: string): Promise { + const answer: AutomxRows = { database: AUTOMX_DATABASE, tables: {}, empty_names: [], unreadable: {} }; + for (const [table, sql] of Object.entries(AUTOMX_TABLES)) { + try { + answer.tables[table] = parseRows(await exec(container, readCommand(sql))); + } catch (e) { + const err = e as { stderr?: string; message?: string }; + const text = (err?.stderr ?? "").trim() || (err?.message ?? String(e)).trim(); + answer.unreadable[table] = text.split("\n").map((l) => l.trim()).filter(Boolean)[0] ?? "it failed and said nothing"; + } + } + for (const s of answer.tables.server ?? []) { + if (String(s.name ?? "").trim() === "") answer.empty_names.push({ id: Number(s.id), type: String(s.type) }); + } + return answer; +} diff --git a/modules/mailu/module.json b/modules/mailu/module.json index 568be145..98c3cb23 100644 --- a/modules/mailu/module.json +++ b/modules/mailu/module.json @@ -71,7 +71,8 @@ "mailu_list_domains", "mailu_read_mail", "mailu_search_mail", - "mailu_health" + "mailu_health", + "automx_rows" ], "emits": [ "user.created", diff --git a/modules/mailu/test/automx-rows.test.ts b/modules/mailu/test/automx-rows.test.ts new file mode 100644 index 00000000..b0d048f5 --- /dev/null +++ b/modules/mailu/test/automx-rows.test.ts @@ -0,0 +1,81 @@ +// What automx_rows reads (novox/hq issue 1630): the rows of automx2's database as stored, through a read-only +// sqlite3 inside its container. The database here is a real SQLite file made by the test with the tables +// automx/files/setup-db creates and the rows add-domains writes — with the IMAP server's name empty, the case +// the live instance is in. The fake container runs each command's statement against that database and prints +// it the way `sqlite3 -json` does (one JSON array, nothing for no rows). Run against the compiled module. + +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { DatabaseSync } from "node:sqlite"; + +import { readAutomx, readCommand, AUTOMX_DATABASE } from "../dist/automx.js"; + +function database(imapName: string): DatabaseSync { + const db = new DatabaseSync(":memory:"); + db.exec(` + CREATE TABLE provider(id INT PRIMARY KEY NOT NULL, name TEXT NOT NULL, short_name TEXT NOT NULL); + CREATE TABLE server(id INT PRIMARY KEY NOT NULL, prio INT NOT NULL DEFAULT 10, name TEXT NOT NULL, port INT NOT NULL, + type TEXT NOT NULL, socket_type TEXT NOT NULL, user_name TEXT NOT NULL, authentication TEXT NOT NULL); + CREATE TABLE domain(id INT PRIMARY KEY NOT NULL, name TEXT NOT NULL, provider_id INT NOT NULL, ldapserver_id INT NULL); + CREATE TABLE server_domain(server_id INT NOT NULL, domain_id INT NOT NULL); + INSERT INTO provider VALUES (1, 'example.org', 'example.org'); + INSERT INTO server(id, port, type, name, socket_type, user_name, authentication) + VALUES (2, 143, 'imap', '${imapName}', 'STARTTLS', '%EMAILADDRESS%', 'password-cleartext'); + INSERT INTO server(id, port, type, name, socket_type, user_name, authentication) + VALUES (3, 587, 'smtp', 'mail.example.org', 'STARTTLS', '%EMAILADDRESS%', 'password-cleartext'); + INSERT INTO domain VALUES (5, 'example.org', 1, NULL); + INSERT INTO server_domain VALUES (2, 5), (3, 5); + `); + return db; +} + +function container(db: DatabaseSync, asked: string[][] = []) { + return async (_name: string, command: string[]) => { + asked.push(command); + const rows = db.prepare(command[command.length - 1]).all(); + return rows.length === 0 ? "" : JSON.stringify(rows) + "\n"; + }; +} + +test("the rows come back as stored, and an empty server name is said", async () => { + const answer = await readAutomx(container(database("")), "mailu-automx"); + assert.equal(answer.database, AUTOMX_DATABASE); + assert.deepEqual(answer.unreadable, {}); + assert.deepEqual(answer.tables.provider, [{ id: 1, name: "example.org", short_name: "example.org" }]); + assert.deepEqual(answer.tables.domain, [{ id: 5, name: "example.org", provider_id: 1, ldapserver_id: null }]); + assert.deepEqual(answer.tables.server_domain, [{ server_id: 2, domain_id: 5 }, { server_id: 3, domain_id: 5 }]); + assert.deepEqual( + answer.tables.server.map((s) => [s.id, s.type, s.name, s.port]), + [[2, "imap", "", 143], [3, "smtp", "mail.example.org", 587]], + ); + assert.deepEqual(answer.empty_names, [{ id: 2, type: "imap" }]); +}); + +test("a named IMAP server is not reported empty", async () => { + const answer = await readAutomx(container(database("mail.example.org")), "mailu-automx"); + assert.deepEqual(answer.empty_names, []); +}); + +test("every command opens the file read-only and runs one fixed SELECT", async () => { + const asked: string[][] = []; + await readAutomx(container(database("x"), asked), "mailu-automx"); + assert.equal(asked.length, 4); + for (const c of asked) { + assert.deepEqual(c.slice(0, 4), ["sqlite3", "-readonly", "-json", AUTOMX_DATABASE]); + assert.match(c[4], /^SELECT /); + assert.equal(c.length, 5); + } + assert.deepEqual(readCommand("SELECT 1").length, 5); +}); + +test("a table that cannot be read is said and does not hide the others", async () => { + const db = database("x"); + const exec = async (name: string, command: string[]) => { + if (command[4].includes("FROM domain ")) throw Object.assign(new Error("Command failed: docker exec"), { stderr: "Error: unable to open database file\nmore" }); + return container(db)(name, command); + }; + const answer = await readAutomx(exec, "mailu-automx"); + assert.deepEqual(answer.unreadable, { domain: "Error: unable to open database file" }); + assert.equal(answer.tables.provider.length, 1); + assert.equal(answer.tables.domain, undefined); +}); diff --git a/modules/mailu/test/mail-flows.test.ts b/modules/mailu/test/mail-flows.test.ts index ddea9791..3950148c 100644 --- a/modules/mailu/test/mail-flows.test.ts +++ b/modules/mailu/test/mail-flows.test.ts @@ -135,6 +135,7 @@ test("the health tool is served without the admin API, on the smtp container it await import("../dist/tools/index.js"); const mailu = collectTools({ MESH_MAILU_SMTP_CONTAINER: "mailu-smtp" }).find((m) => m.module === "mailu"); const names = (mailu?.tools ?? []).map((t) => t.name); - assert.deepEqual(names, ["mailu_health"]); + assert.deepEqual(names, ["mailu_health", "automx_rows"]); assert.match(mailu!.tools[0].description, /inside mailu-smtp/); + assert.match(mailu!.tools[1].description, /read-only, inside mailu-automx/); }); diff --git a/modules/mailu/tools/index.ts b/modules/mailu/tools/index.ts index 6b8442e9..c6624f7e 100644 --- a/modules/mailu/tools/index.ts +++ b/modules/mailu/tools/index.ts @@ -4,6 +4,7 @@ import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools"; import { MailuClient } from "../client.js"; +import { readAutomx } from "../automx.js"; import { dockerExec, mailFlows, DEFERRED_BOUND_SECONDS, PUBLIC_NAME, SPREAD_DOMAINS } from "../health.js"; /** @@ -26,6 +27,23 @@ export function getHealthTool(env: NodeJS.ProcessEnv): ToolDefinition { }; } +/** + * What automx holds, as stored (novox/hq issue 1630): the rows behind the answers it gives mail clients. It needs + * no admin API, only the automx container, so like the health tool it is served without the API. + */ +export function getAutomxTool(env: NodeJS.ProcessEnv): ToolDefinition { + const container = env.MESH_MAILU_AUTOMX_CONTAINER || "mailu-automx"; + return { + name: "automx_rows", + description: + `Read the autoconfiguration module's database, as stored, read-only, inside ${container}: its provider, ` + + "server, domain and server_domain rows, and the servers whose name is empty (a mail client is then told " + + "an empty host name). Writes nothing.", + input: {}, + run: async () => readAutomx(dockerExec(), container), + }; +} + export function getMailuTools(mailu: MailuClient): ToolDefinition[] { return [ { @@ -173,5 +191,5 @@ registerModuleTools("mailu", (env) => { } catch { tools = []; } - return [...tools, getHealthTool(env)]; + return [...tools, getHealthTool(env), getAutomxTool(env)]; }); diff --git a/modules/mailu/tsconfig.json b/modules/mailu/tsconfig.json index a55a92ee..ad97ec61 100644 --- a/modules/mailu/tsconfig.json +++ b/modules/mailu/tsconfig.json @@ -8,5 +8,5 @@ "skipLibCheck": true, "noEmit": true }, - "include": ["client.ts", "health.ts", "index.ts", "tools/index.ts"] + "include": ["client.ts", "health.ts", "automx.ts", "index.ts", "tools/index.ts"] }