add Content-Disposition and Filename to the payload of incoming webhooks

for each message part. The ContentDisposition value is the base value without
header key/value parameters. the Filename field is the likely filename of the
part. the different email clients encode filenames differently. there is a
standard mime mechanism from rfc 2231. and there is the q/b-word-encoding from
rfc 2047. instead of letting users of the webhook api deal with those
differences, we provide just the parsed filename.

for issue #258 by morki, thanks for reporting!
This commit is contained in:
Mechiel Lukkien
2024-12-06 14:19:39 +01:00
parent 8804d6b60e
commit 42793834f8
15 changed files with 170 additions and 52 deletions

View File

@ -2,6 +2,7 @@ package webmail
import (
"bufio"
"errors"
"fmt"
"io"
"log/slog"
@ -275,21 +276,16 @@ func parsedMessage(log mlog.Log, m store.Message, state *msgState, full, msgitem
case "TEXT/PLAIN", "/":
// Don't include if Content-Disposition attachment.
if full || msgitem {
// todo: should have this, and perhaps all content-* headers, preparsed in message.Part?
h, err := p.Header()
log.Check(err, "parsing attachment headers", slog.Int64("msgid", m.ID))
cp := h.Get("Content-Disposition")
if cp != "" {
disp, params, err := mime.ParseMediaType(cp)
log.Check(err, "parsing content-disposition", slog.String("cp", cp))
if strings.EqualFold(disp, "attachment") {
name := tryDecodeParam(log, p.ContentTypeParams["name"])
if name == "" {
name = tryDecodeParam(log, params["filename"])
}
addAttachment(Attachment{path, name, p}, parentMixed)
return
}
disp, name, err := p.DispositionFilename()
if err != nil && errors.Is(err, message.ErrParamEncoding) {
log.Debugx("parsing disposition/filename", err)
} else if err != nil {
rerr = fmt.Errorf("reading disposition/filename: %v", err)
return
}
if strings.EqualFold(disp, "attachment") {
addAttachment(Attachment{path, name, p}, parentMixed)
return
}
}