chore: release v0.19.3
This commit is contained in:
+70
-6
@@ -61,9 +61,12 @@ const (
|
||||
eqslBaseURL = "https://www.eQSL.cc"
|
||||
)
|
||||
|
||||
// eqslAdiHrefRe pulls the generated .adi path out of the DownloadInBox reply,
|
||||
// e.g. `<A HREF="/qslcard/downloadedfiles/xxxx.adi">`.
|
||||
var eqslAdiHrefRe = regexp.MustCompile(`(?i)href="(/[^"]+\.adi)"`)
|
||||
// eqslAdiHrefRe pulls the generated .adi link out of the DownloadInBox reply.
|
||||
// eQSL serves old HTML 4.01 with UPPERCASE tags and often UNQUOTED attributes,
|
||||
// e.g. `<A HREF=/downloadedFiles/xxxx.adi>` or `<a href="…/xxxx.adi">`, and the
|
||||
// path may be root-relative or a bare filename. Match case-insensitively with
|
||||
// optional quoting; resolve to an absolute URL afterwards.
|
||||
var eqslAdiHrefRe = regexp.MustCompile(`(?i)href\s*=\s*["']?([^"'>\s]+\.adi)`)
|
||||
|
||||
// DownloadEQSLConfirmations fetches the account's Inbox (received eQSLs =
|
||||
// confirmations) as ADIF text. since is "YYYY-MM-DD" (only QSLs received since
|
||||
@@ -97,18 +100,79 @@ func DownloadEQSLConfirmations(ctx context.Context, client *http.Client, cfg Ser
|
||||
}
|
||||
m := eqslAdiHrefRe.FindStringSubmatch(page)
|
||||
if m == nil {
|
||||
// eQSL reports problems inline ("Error: …", or "no QSLs" wording).
|
||||
return "", fmt.Errorf("eqsl: no download link in response: %s", eqslReason(page))
|
||||
// eQSL reports problems inline ("Error: …", "You have no … confirmations").
|
||||
return "", fmt.Errorf("eqsl: no download link in response: %s", eqslNoLinkReason(page))
|
||||
}
|
||||
|
||||
// Resolve the link to an absolute URL. eQSL is a Windows/ColdFusion server, so
|
||||
// the path may use BACKSLASHES and a leading `..` (e.g. `..\downloadedFiles\
|
||||
// xxx.adi`); normalise those. It may also be absolute or a bare filename.
|
||||
link := strings.TrimSpace(m[1])
|
||||
link = strings.ReplaceAll(link, `\`, "/")
|
||||
link = strings.TrimPrefix(link, "..") // "../downloadedFiles/…" → "/downloadedFiles/…"
|
||||
var fileURL string
|
||||
switch {
|
||||
case strings.HasPrefix(strings.ToLower(link), "http"):
|
||||
fileURL = link
|
||||
case strings.HasPrefix(link, "/"):
|
||||
fileURL = eqslBaseURL + link
|
||||
default:
|
||||
fileURL = eqslBaseURL + "/" + link
|
||||
}
|
||||
|
||||
// Step 2: fetch the generated .adi file.
|
||||
adif, err := eqslGet(ctx, client, eqslBaseURL+m[1])
|
||||
adif, err := eqslGet(ctx, client, fileURL)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("eqsl: fetch adif: %w", err)
|
||||
}
|
||||
return adif, nil
|
||||
}
|
||||
|
||||
// eqslNoLinkReason builds a helpful message when no .adi link was found: eQSL's
|
||||
// own error/notice text if present, else a short tag-stripped snippet of the page
|
||||
// so the real wording (e.g. "You have no Inbox items") is visible in the log.
|
||||
func eqslNoLinkReason(page string) string {
|
||||
low := strings.ToLower(page)
|
||||
for _, marker := range []string{"you have no", "no inbox", "error", "invalid", "not found"} {
|
||||
if i := strings.Index(low, marker); i >= 0 {
|
||||
seg := page[i:]
|
||||
if len(seg) > 160 {
|
||||
seg = seg[:160]
|
||||
}
|
||||
return strings.Join(strings.Fields(stripTags(seg)), " ")
|
||||
}
|
||||
}
|
||||
txt := strings.Join(strings.Fields(stripTags(page)), " ")
|
||||
if len(txt) > 200 {
|
||||
txt = txt[:200]
|
||||
}
|
||||
if txt == "" {
|
||||
txt = "empty response"
|
||||
}
|
||||
return txt
|
||||
}
|
||||
|
||||
// stripTags removes HTML tags for a readable one-line diagnostic.
|
||||
func stripTags(s string) string {
|
||||
var b strings.Builder
|
||||
depth := 0
|
||||
for _, r := range s {
|
||||
switch r {
|
||||
case '<':
|
||||
depth++
|
||||
case '>':
|
||||
if depth > 0 {
|
||||
depth--
|
||||
}
|
||||
default:
|
||||
if depth == 0 {
|
||||
b.WriteRune(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// eqslGet does a plain GET and returns the body as text (32 MB cap), erroring on
|
||||
// a non-200 status.
|
||||
func eqslGet(ctx context.Context, client *http.Client, u string) (string, error) {
|
||||
|
||||
Reference in New Issue
Block a user