Skip to content

Commit

Permalink
webmail: put attached files before inline files
Browse files Browse the repository at this point in the history
some emails have text and html versions. the html can have several logo images.
and there may be a pdf attached. when gathering attachments to show in webmail,
the pdf would come last. it could happen the logo images would get a link to
click, and the pdf would be behind the "more ..." button. by putting
"multipart/mixed" files before the "multipart/related" in the list, it's more
likely that useful files can be clicked immediately, and unimportant logo files
are behind the "more"-button.
  • Loading branch information
mjl- committed Aug 5, 2024
1 parent 0a4999f commit 2c00399
Showing 1 changed file with 28 additions and 8 deletions.
36 changes: 28 additions & 8 deletions webmail/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,18 +240,34 @@ func parsedMessage(log mlog.Log, m store.Message, state *msgState, full, msgitem
}

pm.Texts = []string{}
pm.attachments = []Attachment{}

// We track attachments from multipart/mixed differently from other attachments.
// The others are often inline, sometimes just some logo's in HTML alternative
// messages. We want to have our mixed attachments at the start of the list, but
// our descent-first parsing would result in inline messages first in the typical
// message.
var attachmentsMixed []Attachment
var attachmentsOther []Attachment

addAttachment := func(a Attachment, isMixed bool) {
if isMixed {
attachmentsMixed = append(attachmentsMixed, a)
} else {
attachmentsOther = append(attachmentsOther, a)
}
}

// todo: how should we handle messages where a user prefers html, and we want to show it, but it's a DSN that also has textual-only parts? e.g. gmail's dsn where the first part is multipart/related with multipart/alternative, and second part is the regular message/delivery-status. we want to display both the html and the text.

var usePart func(p message.Part, index int, parent *message.Part, path []int)
usePart = func(p message.Part, index int, parent *message.Part, path []int) {
var usePart func(p message.Part, index int, parent *message.Part, path []int, parentMixed bool)
usePart = func(p message.Part, index int, parent *message.Part, path []int, parentMixed bool) {
mt := p.MediaType + "/" + p.MediaSubType
newParentMixed := mt == "MULTIPART/MIXED"
for i, sp := range p.Parts {
if mt == "MULTIPART/SIGNED" && i >= 1 {
continue
}
usePart(sp, i, &p, append(append([]int{}, path...), i))
usePart(sp, i, &p, append(append([]int{}, path...), i), newParentMixed)
}
switch mt {
case "TEXT/PLAIN", "/":
Expand All @@ -269,7 +285,7 @@ func parsedMessage(log mlog.Log, m store.Message, state *msgState, full, msgitem
if name == "" {
name = tryDecodeParam(log, params["filename"])
}
pm.attachments = append(pm.attachments, Attachment{path, name, p})
addAttachment(Attachment{path, name, p}, parentMixed)
return
}
}
Expand Down Expand Up @@ -333,7 +349,7 @@ func parsedMessage(log mlog.Log, m store.Message, state *msgState, full, msgitem
return
}
if parentct == "MULTIPART/REPORT" && index == 2 && (mt == "MESSAGE/GLOBAL" || mt == "TEXT/RFC822") {
pm.attachments = append(pm.attachments, Attachment{path, "original.eml", p})
addAttachment(Attachment{path, "original.eml", p}, parentMixed)
return
}

Expand All @@ -349,11 +365,15 @@ func parsedMessage(log mlog.Log, m store.Message, state *msgState, full, msgitem
name = tryDecodeParam(log, params["filename"])
}
}
pm.attachments = append(pm.attachments, Attachment{path, name, p})
addAttachment(Attachment{path, name, p}, parentMixed)
}
}
}
usePart(*state.part, -1, nil, []int{})
usePart(*state.part, -1, nil, []int{}, false)

pm.attachments = []Attachment{}
pm.attachments = append(pm.attachments, attachmentsMixed...)
pm.attachments = append(pm.attachments, attachmentsOther...)

if rerr == nil {
pm.ID = m.ID
Expand Down

0 comments on commit 2c00399

Please sign in to comment.