From 1ac00c101ed647904ae4c45da20dad53a0b93b5d Mon Sep 17 00:00:00 2001 From: Gregory Salaun Date: Tue, 11 Aug 2026 15:34:06 +0200 Subject: [PATCH] fix(clublog): say what is wrong, not four kilobytes of markup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Club Log refuses with its ordinary web page rather than an error string, so the new test reported a rejected login by pasting a 403 page — title, stylesheets, navigation and all — into the status bar. The body now goes to the log, where a real diagnosis happens, and the operator gets the one sentence there is to act on: check the e-mail, the password and the logbook callsign. Dropped the startyear=2099 filter with it, and that one matters more than it looks. It was there to keep the reply small, but it was never verified against Club Log's API — and Club Log answers an unrecognised request with the SAME 403 it uses for a refused login. An unverified parameter would therefore have made every CORRECT password look wrong, which is precisely the failure this change set out to end. The reply is capped at 4 KB and closed at once instead; the status code arrives ahead of the body either way. --- internal/extsvc/clublog.go | 33 +++++++++++++++++++++++++-------- internal/extsvc/clublog_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 internal/extsvc/clublog_test.go diff --git a/internal/extsvc/clublog.go b/internal/extsvc/clublog.go index c0719a2..66bce77 100644 --- a/internal/extsvc/clublog.go +++ b/internal/extsvc/clublog.go @@ -27,6 +27,14 @@ const clublogBatchURL = "https://clublog.org/putlogs.php" // must send a real, app-identifying User-Agent. const clublogUserAgent = "OpsLog/1.0 (+https://github.com/GregTroar/OpsLog)" +// looksLikeHTML reports a body that is a web page rather than an answer. Club +// Log serves its normal site for refusals and blocks, so this is what separates +// "here is what went wrong" from 4 KB of markup an operator cannot act on. +func looksLikeHTML(s string) bool { + l := strings.ToLower(strings.TrimSpace(s)) + return strings.HasPrefix(l, " 200 { msg = msg[:200] + "…" } diff --git a/internal/extsvc/clublog_test.go b/internal/extsvc/clublog_test.go new file mode 100644 index 0000000..709f592 --- /dev/null +++ b/internal/extsvc/clublog_test.go @@ -0,0 +1,26 @@ +package extsvc + +import "testing" + +// Club Log answers a refused login with its ordinary web page. The operator must +// get a sentence they can act on, not four kilobytes of markup — that was the +// first thing reported once the test started working at all. +func TestLooksLikeHTML(t *testing.T) { + cases := []struct { + body string + want bool + }{ + {"\n…403 - Access denied…", true}, + {" 403", true}, + {"", true}, + {"Invalid credentials", false}, + {"", false}, + // An ADIF answer must never be mistaken for a page. + {"\nF4BPO 20m ", false}, + } + for _, c := range cases { + if got := looksLikeHTML(c.body); got != c.want { + t.Errorf("looksLikeHTML(%.40q) = %v, want %v", c.body, got, c.want) + } + } +}