package listunsubscribe import ( "net/mail" "net/url" "strings" ) // Mailto holds a one-click mailto unsubscribe target. type Mailto struct { Address string Subject string Body string } // Parsed from List-Unsubscribe (RFC 2369). type Parsed struct { Mailto *Mailto HTTP string } func splitHeaderParts(raw string) []string { var parts []string var cur strings.Builder depth := 0 for _, r := range raw { switch r { case '<': depth++ cur.WriteRune(r) case '>': if depth > 0 { depth-- } cur.WriteRune(r) case ',': if depth == 0 { if p := strings.TrimSpace(cur.String()); p != "" { parts = append(parts, p) } cur.Reset() continue } cur.WriteRune(r) default: cur.WriteRune(r) } } if p := strings.TrimSpace(cur.String()); p != "" { parts = append(parts, p) } return parts } func unwrapAngle(s string) string { s = strings.TrimSpace(s) if strings.HasPrefix(s, "<") && strings.HasSuffix(s, ">") { return strings.TrimSpace(s[1 : len(s)-1]) } return s } // ParseMailtoURL parses mailto:user@host?subject=...&body=... func ParseMailtoURL(raw string) (*Mailto, bool) { raw = unwrapAngle(strings.TrimSpace(raw)) if raw == "" { return nil, false } if !strings.HasPrefix(strings.ToLower(raw), "mailto:") { return nil, false } u, err := url.Parse(raw) if err != nil { return nil, false } addr := strings.TrimSpace(u.Opaque) if addr == "" { addr = strings.TrimSpace(strings.TrimPrefix(u.Path, "/")) } if addr == "" { return nil, false } if _, err := mail.ParseAddress(addr); err != nil { // bare addr@host if !strings.Contains(addr, "@") { return nil, false } } return &Mailto{ Address: addr, Subject: u.Query().Get("subject"), Body: u.Query().Get("body"), }, true } // Parse extracts mailto and https targets from a List-Unsubscribe header value. func Parse(listUnsubscribe string) Parsed { out := Parsed{} for _, part := range splitHeaderParts(listUnsubscribe) { part = unwrapAngle(part) if m, ok := ParseMailtoURL(part); ok && out.Mailto == nil { out.Mailto = m continue } lower := strings.ToLower(part) if strings.HasPrefix(lower, "http://") || strings.HasPrefix(lower, "https://") { if out.HTTP == "" { out.HTTP = part } } } return out }