package verification import ( "fmt" "io" "net/http" "net/url" "regexp" "strings" "time" "margin.at/internal/logger" ) var client = &http.Client{ Timeout: 10 * time.Second, CheckRedirect: func(req *http.Request, via []*http.Request) error { if len(via) >= 3 { return fmt.Errorf("too many redirects") } return nil }, } var linkTagPattern = regexp.MustCompile(`]+rel=["']site\.standard\.document["'][^>]+href=["']([^"']+)["'][^>]*/?>|]+href=["']([^"']+)["'][^>]+rel=["']site\.standard\.document["'][^>]*/?>`) func VerifyPublication(pubURL, expectedURI string) error { pubURL = strings.TrimRight(pubURL, "/") parsed, err := url.Parse(pubURL) if err != nil { return fmt.Errorf("invalid publication URL: %w", err) } wellKnownPath := "/.well-known/site.standard.publication" if parsed.Path != "" && parsed.Path != "/" { wellKnownPath += parsed.Path } wellKnownURL := fmt.Sprintf("%s://%s%s", parsed.Scheme, parsed.Host, wellKnownPath) req, err := http.NewRequest("GET", wellKnownURL, nil) if err != nil { return fmt.Errorf("invalid URL: %w", err) } req.Header.Set("User-Agent", "Margin/1.0 (Standard.site verification)") resp, err := client.Do(req) if err != nil { return fmt.Errorf("failed to fetch %s: %w", wellKnownURL, err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("well-known endpoint returned %d", resp.StatusCode) } body, err := io.ReadAll(io.LimitReader(resp.Body, 1024)) if err != nil { return fmt.Errorf("failed to read response: %w", err) } returnedURI := strings.TrimSpace(string(body)) if returnedURI != expectedURI { return fmt.Errorf("URI mismatch: expected %s, got %s", expectedURI, returnedURI) } return nil } func VerifyDocument(docURL, expectedURI string) error { req, err := http.NewRequest("GET", docURL, nil) if err != nil { return fmt.Errorf("invalid URL: %w", err) } req.Header.Set("User-Agent", "Margin/1.0 (Standard.site verification)") resp, err := client.Do(req) if err != nil { return fmt.Errorf("failed to fetch %s: %w", docURL, err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("document URL returned %d", resp.StatusCode) } body, err := io.ReadAll(io.LimitReader(resp.Body, 256*1024)) if err != nil { return fmt.Errorf("failed to read document: %w", err) } html := string(body) matches := linkTagPattern.FindAllStringSubmatch(html, -1) for _, m := range matches { href := m[1] if href == "" { href = m[2] } if strings.TrimSpace(href) == expectedURI { return nil } } return fmt.Errorf("no matching tag found for %s", expectedURI) } func VerifyPublicationAsync(pubURL, uri string, onVerified func(string)) { go func() { if err := VerifyPublication(pubURL, uri); err != nil { return } logger.Info("Publication verified: %s", uri) if onVerified != nil { onVerified(uri) } }() } func VerifyDocumentAsync(docURL, uri string, onVerified func(string)) { go func() { if err := VerifyDocument(docURL, uri); err != nil { return } logger.Info("Document verified: %s", uri) if onVerified != nil { onVerified(uri) } }() }