// Package linkcheck handles message links checking package linkcheck import ( "regexp" "strings" "github.com/PuerkitoBio/goquery" "github.com/axllent/mailpit/internal/storage" "github.com/axllent/mailpit/internal/tools" ) var linkRe = regexp.MustCompile(`(?m)\b(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:'!\/~+#-]*[\w@?^=%&\/~+#-])`) // RunTests will run all tests on an HTML string func RunTests(msg *storage.Message, followRedirects bool) (Response, error) { s := Response{} allLinks := extractHTMLLinks(msg) allLinks = strUnique(append(allLinks, extractTextLinks(msg)...)) s.Links = getHTTPStatuses(allLinks, followRedirects) for _, l := range s.Links { if l.StatusCode >= 400 || l.StatusCode == 0 { s.Errors++ } } return s, nil } func extractTextLinks(msg *storage.Message) []string { links := []string{} for _, match := range linkRe.FindAllString(msg.Text, -1) { links = append(links, match) } return links } func extractHTMLLinks(msg *storage.Message) []string { links := []string{} reader := strings.NewReader(msg.HTML) // Load the HTML document doc, err := goquery.NewDocumentFromReader(reader) if err != nil { return links } aLinks := doc.Find("a[href]").Nodes for _, link := range aLinks { l, err := tools.GetHTMLAttributeVal(link, "href") if err == nil && linkRe.MatchString(l) { links = append(links, l) } } cssLinks := doc.Find("link[rel=\"stylesheet\"]").Nodes for _, link := range cssLinks { l, err := tools.GetHTMLAttributeVal(link, "href") if err == nil && linkRe.MatchString(l) { links = append(links, l) } } imgLinks := doc.Find("img[src]").Nodes for _, link := range imgLinks { l, err := tools.GetHTMLAttributeVal(link, "src") if err == nil && linkRe.MatchString(l) { links = append(links, l) } } return links } // strUnique return a slice of unique strings from a slice func strUnique(strSlice []string) []string { keys := make(map[string]bool) list := []string{} for _, entry := range strSlice { if _, value := keys[entry]; !value { keys[entry] = true list = append(list, entry) } } return list }