fixing folders and image rendering in client
This commit is contained in:
@@ -30,6 +30,11 @@ type Attachment struct {
|
||||
Filename string
|
||||
ContentType string
|
||||
Data []byte
|
||||
// ContentID is the part's Content-Id header (RFC 2392), angle brackets stripped,
|
||||
// or "" if absent. An HTML body referencing this part inline (<img src="cid:...">)
|
||||
// only renders once that reference is resolved against this — see
|
||||
// webui.inlineContentIDImages, the caller responsible for doing so.
|
||||
ContentID string
|
||||
}
|
||||
|
||||
// Message is the parsed result. TextBody/HTMLBody are independently populated when
|
||||
@@ -114,11 +119,12 @@ func walkMultipart(m *Message, r io.Reader, boundary string) error {
|
||||
if filename == "" {
|
||||
filename = params["name"]
|
||||
}
|
||||
contentID := strings.Trim(part.Header.Get("Content-Id"), "<>")
|
||||
|
||||
switch {
|
||||
case disp == "attachment" || (filename != "" && disp != "inline"):
|
||||
m.Attachments = append(m.Attachments, Attachment{
|
||||
Filename: filename, ContentType: contentTypeFor(mediaType, filename), Data: data,
|
||||
Filename: filename, ContentType: contentTypeFor(mediaType, filename), Data: data, ContentID: contentID,
|
||||
})
|
||||
case mediaType == "text/html":
|
||||
m.HTMLBody += string(data)
|
||||
@@ -127,12 +133,15 @@ func walkMultipart(m *Message, r io.Reader, boundary string) error {
|
||||
m.TextBody += "\n"
|
||||
}
|
||||
m.TextBody += string(data)
|
||||
case filename != "":
|
||||
// Inline non-text part (e.g. an embedded image) with no explicit
|
||||
// disposition — still worth surfacing as a downloadable attachment
|
||||
// rather than silently dropping it.
|
||||
case filename != "" || contentID != "":
|
||||
// Inline non-text part (e.g. an embedded image referenced by the HTML
|
||||
// body via cid:) with no explicit disposition and possibly no filename
|
||||
// either — still worth surfacing rather than silently dropping it.
|
||||
if filename == "" {
|
||||
filename = contentID
|
||||
}
|
||||
m.Attachments = append(m.Attachments, Attachment{
|
||||
Filename: filename, ContentType: contentTypeFor(mediaType, filename), Data: data,
|
||||
Filename: filename, ContentType: contentTypeFor(mediaType, filename), Data: data, ContentID: contentID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,3 +89,49 @@ func TestParseNestedMultipartMixedWithAlternativeBody(t *testing.T) {
|
||||
t.Fatalf("attachments = %+v", m.Attachments)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseInlineImageCapturesContentID(t *testing.T) {
|
||||
raw := "" +
|
||||
"From: a@example.com\r\nTo: b@example.com\r\nSubject: hi\r\n" +
|
||||
"Content-Type: multipart/related; boundary=\"B\"\r\n\r\n" +
|
||||
"--B\r\nContent-Type: text/html\r\n\r\n<p><img src=\"cid:img1@example.com\"></p>\r\n" +
|
||||
"--B\r\nContent-Type: image/png\r\nContent-Disposition: inline; filename=\"header.png\"\r\n" +
|
||||
"Content-Id: <img1@example.com>\r\n" +
|
||||
"Content-Transfer-Encoding: BASE64\r\n\r\nSGVsbG8sIHdvcmxkIQ==\r\n" +
|
||||
"--B--\r\n"
|
||||
m, err := Parse([]byte(raw))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.TrimSpace(m.HTMLBody) != `<p><img src="cid:img1@example.com"></p>` {
|
||||
t.Errorf("HTMLBody = %q", m.HTMLBody)
|
||||
}
|
||||
if len(m.Attachments) != 1 {
|
||||
t.Fatalf("attachments = %+v", m.Attachments)
|
||||
}
|
||||
if m.Attachments[0].ContentID != "img1@example.com" {
|
||||
t.Errorf("ContentID = %q, want angle brackets stripped", m.Attachments[0].ContentID)
|
||||
}
|
||||
if got := string(m.Attachments[0].Data); got != "Hello, world!" {
|
||||
t.Errorf("attachment data = %q, want decoded base64", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseInlineImageWithNoFilenameStillCaptured(t *testing.T) {
|
||||
raw := "" +
|
||||
"From: a@example.com\r\nTo: b@example.com\r\nSubject: hi\r\n" +
|
||||
"Content-Type: multipart/related; boundary=\"B\"\r\n\r\n" +
|
||||
"--B\r\nContent-Type: text/html\r\n\r\n<p>hi</p>\r\n" +
|
||||
"--B\r\nContent-Type: image/png\r\n" +
|
||||
"Content-Id: <noname@example.com>\r\n\r\nrawbytes" +
|
||||
"\r\n--B--\r\n"
|
||||
m, err := Parse([]byte(raw))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// No filename anywhere on the part — must not be silently dropped just because
|
||||
// there's nothing to name it; falls back to the Content-Id itself.
|
||||
if len(m.Attachments) != 1 || m.Attachments[0].ContentID != "noname@example.com" {
|
||||
t.Fatalf("attachments = %+v", m.Attachments)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user