diff --git a/internal/htmlutil/htmlutil.go b/internal/htmlutil/htmlutil.go
index a012c501..e192dbb4 100644
--- a/internal/htmlutil/htmlutil.go
+++ b/internal/htmlutil/htmlutil.go
@@ -73,7 +73,7 @@ func ExtractAttachments(s string) []Attachment {
return nil
}
var attachments []Attachment
- findAttachments(doc, &attachments)
+ findAttachments(doc, &attachments, 0)
return attachments
}
@@ -336,7 +336,7 @@ func parseEmbeddedContent(content string, depth int) *html.Node {
return doc
}
-func findAttachments(n *html.Node, attachments *[]Attachment) {
+func findAttachments(n *html.Node, attachments *[]Attachment, depth int) {
if n.Type == html.ElementNode {
switch n.Data {
case "action-text-attachment":
@@ -352,7 +352,10 @@ func findAttachments(n *html.Node, attachments *[]Attachment) {
*attachments = append(*attachments, attachment)
}
case "figure":
- if trix := parseTrixAttachment(n); trix != nil && trix.URL != "" && trix.Filename != "" {
+ trix := parseTrixAttachment(n)
+ switch {
+ case trix == nil:
+ case trix.URL != "" && trix.Filename != "":
*attachments = append(*attachments, Attachment{
URL: trix.URL,
Filename: trix.Filename,
@@ -360,11 +363,18 @@ func findAttachments(n *html.Node, attachments *[]Attachment) {
ByteSize: nonnegativeAttachmentByteSize(trix.Filesize),
SGID: trix.SGID,
})
+ case trix.Content != "":
+ // An inbound email's files are inside the embedded markup, not
+ // on the figure that wraps it. The wrapper itself is not listed:
+ // an embedded body is not a downloadable file.
+ if doc := parseEmbeddedContent(trix.Content, depth); doc != nil {
+ findAttachments(doc, attachments, depth+1)
+ }
}
}
}
for child := n.FirstChild; child != nil; child = child.NextSibling {
- findAttachments(child, attachments)
+ findAttachments(child, attachments, depth)
}
}
diff --git a/internal/htmlutil/htmlutil_test.go b/internal/htmlutil/htmlutil_test.go
index f5f703b9..c49bd118 100644
--- a/internal/htmlutil/htmlutil_test.go
+++ b/internal/htmlutil/htmlutil_test.go
@@ -194,6 +194,33 @@ func TestExtractAttachmentsSkipsEmbeddedHTMLAttachment(t *testing.T) {
}
}
+func TestExtractAttachmentsInsideEmbeddedHTMLAttachment(t *testing.T) {
+ // An HTML email from outside HEY arrives as one text/html trix attachment
+ // whose content string holds the original markup, files included.
+ content := ``
+
+ attachments := ExtractAttachments(content)
+ if len(attachments) != 1 {
+ t.Fatalf("ExtractAttachments = %+v, want the file inside the embedded body", attachments)
+ }
+ got := attachments[0]
+ if got.Filename != "payslip.pdf" || got.URL != "/rails/blobs/payslip.pdf" || got.ContentType != "application/pdf" || got.SGID != "sgid-1" || got.ByteSize == nil || *got.ByteSize != 44218 {
+ t.Errorf("embedded attachment = %+v", got)
+ }
+}
+
+func TestExtractAttachmentsEmbeddedContentStopsRecursing(t *testing.T) {
+ nested := ``
+ for range embeddedContentDepthLimit + 2 {
+ nested = ``
+ }
+
+ if attachments := ExtractAttachments(nested); len(attachments) != 0 {
+ t.Errorf("ExtractAttachments = %+v, should stop before the innermost level", attachments)
+ }
+}
+
func TestExtractAttachments(t *testing.T) {
h := `
`