From efd6931abb111424c293e24fdcf64e36a6fbed09 Mon Sep 17 00:00:00 2001 From: atsunatsu Date: Mon, 7 Sep 2026 13:51:41 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20LoTW=20sync=20=E2=80=94=20force=20full?= =?UTF-8?q?=20report=20with=20qso=5Fqslsince=20(LoTW=20default=20only=20re?= =?UTF-8?q?turns=20new=20confirmations=20since=20last=20query)=20and=20acc?= =?UTF-8?q?ept=20lowercase=20=20header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../look4sat/core/data/repository/LoTWRepository.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/data/src/main/java/com/rtbishop/look4sat/core/data/repository/LoTWRepository.kt b/core/data/src/main/java/com/rtbishop/look4sat/core/data/repository/LoTWRepository.kt index bc84e79d..b7f9fbb8 100644 --- a/core/data/src/main/java/com/rtbishop/look4sat/core/data/repository/LoTWRepository.kt +++ b/core/data/src/main/java/com/rtbishop/look4sat/core/data/repository/LoTWRepository.kt @@ -44,10 +44,16 @@ class LoTWRepository : ILoTWRepository { val call = callsign.trim().uppercase() val pwd = password.trim() if (call.isBlank() || pwd.isBlank()) return@withContext null + // qso_qslsince with an early date forces a FULL confirmed-QSL report. + // Without it, LoTW applies a "system supplied default" since-date and + // only returns confirmations newer than the account's last query — + // subsequent syncs would return an empty/incremental report. + val since = "2000-01-01" val query = buildString { append("login=").append(URLEncoder.encode(call, "UTF-8")) append("&password=").append(URLEncoder.encode(pwd, "UTF-8")) append("&qso_query=1&qso_qsl=yes&qso_qsldetail=yes&qso_mydetail=yes") + append("&qso_qslsince=").append(URLEncoder.encode(since, "UTF-8")) } try { val connection = URL("$BASE_URL?$query").openConnection() as HttpURLConnection @@ -73,7 +79,10 @@ class LoTWRepository : ILoTWRepository { // LoTW answers with ADIF text; on bad credentials it returns a short error page // containing "password=?" or an -less block. Treat anything without a header // marker as failure so the caller can show a sensible message. - if (!body.contains("")) return null + // LoTW answers with ADIF text; on bad credentials it returns a short error + // page without an header terminator. Real reports always carry + // (LoTW writes it lowercase). Match case-insensitively to be safe. + if (!body.contains("", ignoreCase = true)) return null val grids = mutableSetOf() for (raw in body.lineSequence()) { val line = raw.trim()