msgpart: factorize mime type and filename construction

Reduce code duplication.

Signed-off-by: Robin Jarry <robin@jarry.cc>
Acked-by: Moritz Poldrack <moritz@poldrack.dev>
This commit is contained in:
Robin Jarry 2022-10-12 23:52:45 +02:00
parent a4b80bcc8b
commit 9bd2e0c84f
8 changed files with 30 additions and 28 deletions

View File

@ -198,7 +198,7 @@ func (forward) Execute(aerc *widgets.Aerc, args []string) error {
continue continue
} }
store.FetchBodyPart(msg.Uid, p, func(reader io.Reader) { store.FetchBodyPart(msg.Uid, p, func(reader io.Reader) {
mime := fmt.Sprintf("%s/%s", bs.MIMEType, bs.MIMESubType) mime := bs.FullMIMEType()
params := lib.SetUtf8Charset(bs.Params) params := lib.SetUtf8Charset(bs.Params)
name, ok := params["name"] name, ok := params["name"]
if !ok { if !ok {

View File

@ -204,7 +204,7 @@ func (Recall) Execute(aerc *widgets.Aerc, args []string) error {
continue continue
} }
msg.FetchBodyPart(p, func(reader io.Reader) { msg.FetchBodyPart(p, func(reader io.Reader) {
mime := fmt.Sprintf("%s/%s", bs.MIMEType, bs.MIMESubType) mime := bs.FullMIMEType()
params := lib.SetUtf8Charset(bs.Params) params := lib.SetUtf8Charset(bs.Params)
name, ok := params["name"] name, ok := params["name"]
if !ok { if !ok {

View File

@ -326,7 +326,7 @@ func addMimeType(msg *models.MessageInfo, part []int,
if err != nil { if err != nil {
return err return err
} }
orig.MIMEType = fmt.Sprintf("%s/%s", bs.MIMEType, bs.MIMESubType) orig.MIMEType = bs.FullMIMEType()
return nil return nil
} }

View File

@ -1,7 +1,6 @@
package msgview package msgview
import ( import (
"fmt"
"io" "io"
"mime" "mime"
"os" "os"
@ -52,7 +51,7 @@ func (Open) Execute(aerc *widgets.Aerc, args []string) error {
// try to determine the correct extension based on mimetype // try to determine the correct extension based on mimetype
if part, err := mv.MessageView().BodyStructure().PartAtIndex(p.Index); err == nil { if part, err := mv.MessageView().BodyStructure().PartAtIndex(p.Index); err == nil {
mimeType = fmt.Sprintf("%s/%s", part.MIMEType, part.MIMESubType) mimeType = part.FullMIMEType()
if exts, _ := mime.ExtensionsByType(mimeType); len(exts) > 0 { if exts, _ := mime.ExtensionsByType(mimeType); len(exts) > 0 {
extension = exts[0] extension = exts[0]
} }

View File

@ -215,12 +215,7 @@ func isAbsPath(path string) bool {
// generateFilename tries to get the filename from the given part. // generateFilename tries to get the filename from the given part.
// if that fails it will fallback to a generated one based on the date // if that fails it will fallback to a generated one based on the date
func generateFilename(part *models.BodyStructure) string { func generateFilename(part *models.BodyStructure) string {
var filename string filename := part.FileName()
if fn, ok := part.DispositionParams["filename"]; ok {
filename = fn
} else if fn, ok := part.Params["name"]; ok {
filename = fn
}
// Some MUAs send attachments with names like /some/stupid/idea/happy.jpeg // Some MUAs send attachments with names like /some/stupid/idea/happy.jpeg
// Assuming non hostile intent it does make sense to use just the last // Assuming non hostile intent it does make sense to use just the last
// portion of the pathname as the filename for saving it. // portion of the pathname as the filename for saving it.

View File

@ -9,8 +9,7 @@ import (
func FindPlaintext(bs *models.BodyStructure, path []int) []int { func FindPlaintext(bs *models.BodyStructure, path []int) []int {
for i, part := range bs.Parts { for i, part := range bs.Parts {
cur := append(path, i+1) //nolint:gocritic // intentional append to different slice cur := append(path, i+1) //nolint:gocritic // intentional append to different slice
if strings.ToLower(part.MIMEType) == "text" && if part.FullMIMEType() == "text/plain" {
strings.ToLower(part.MIMESubType) == "plain" {
return cur return cur
} }
if strings.ToLower(part.MIMEType) == "multipart" { if strings.ToLower(part.MIMEType) == "multipart" {
@ -25,8 +24,7 @@ func FindPlaintext(bs *models.BodyStructure, path []int) []int {
func FindCalendartext(bs *models.BodyStructure, path []int) []int { func FindCalendartext(bs *models.BodyStructure, path []int) []int {
for i, part := range bs.Parts { for i, part := range bs.Parts {
cur := append(path, i+1) //nolint:gocritic // intentional append to different slice cur := append(path, i+1) //nolint:gocritic // intentional append to different slice
if strings.ToLower(part.MIMEType) == "text" && if part.FullMIMEType() == "text/calendar" {
strings.ToLower(part.MIMESubType) == "calendar" {
return cur return cur
} }
if strings.ToLower(part.MIMEType) == "multipart" { if strings.ToLower(part.MIMEType) == "multipart" {

View File

@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"strings"
"time" "time"
"github.com/emersion/go-message/mail" "github.com/emersion/go-message/mail"
@ -175,6 +176,21 @@ func (bs *BodyStructure) PartAtIndex(index []int) (*BodyStructure, error) {
return bs.Parts[curidx].PartAtIndex(rest) return bs.Parts[curidx].PartAtIndex(rest)
} }
func (bs *BodyStructure) FullMIMEType() string {
mime := fmt.Sprintf("%s/%s", bs.MIMEType, bs.MIMESubType)
return strings.ToLower(mime)
}
func (bs *BodyStructure) FileName() string {
if filename, ok := bs.DispositionParams["filename"]; ok {
return filename
} else if filename, ok := bs.Params["name"]; ok {
// workaround golang not supporting RFC2231 besides ASCII and UTF8
return filename
}
return ""
}
type Envelope struct { type Envelope struct {
Date time.Time Date time.Time
Subject string Subject string

View File

@ -230,8 +230,7 @@ func createSwitcher(acct *AccountView, switcher *PartSwitcher,
if switcher.selected == -1 && pv.part.MIMEType != "multipart" { if switcher.selected == -1 && pv.part.MIMEType != "multipart" {
switcher.selected = i switcher.selected = i
} }
mime := strings.ToLower(pv.part.MIMEType) + mime := pv.part.FullMIMEType()
"/" + strings.ToLower(pv.part.MIMESubType)
for idx, m := range conf.Viewer.Alternatives { for idx, m := range conf.Viewer.Alternatives {
if m != mime { if m != mime {
continue continue
@ -422,13 +421,9 @@ func (ps *PartSwitcher) Draw(ctx *ui.Context) {
style = ps.mv.uiConfig.GetStyleSelected(config.STYLE_DEFAULT) style = ps.mv.uiConfig.GetStyleSelected(config.STYLE_DEFAULT)
} }
ctx.Fill(0, y+i, ctx.Width(), 1, ' ', style) ctx.Fill(0, y+i, ctx.Width(), 1, ' ', style)
name := fmt.Sprintf("%s/%s", name := part.part.FullMIMEType()
strings.ToLower(part.part.MIMEType), filename := part.part.FileName()
strings.ToLower(part.part.MIMESubType)) if filename != "" {
if filename, ok := part.part.DispositionParams["filename"]; ok {
name += fmt.Sprintf(" (%s)", filename)
} else if filename, ok := part.part.Params["name"]; ok {
// workaround golang not supporting RFC2231 besides ASCII and UTF8
name += fmt.Sprintf(" (%s)", filename) name += fmt.Sprintf(" (%s)", filename)
} }
ctx.Printf(len(part.index)*2, y+i, style, "%s", name) ctx.Printf(len(part.index)*2, y+i, style, "%s", name)
@ -547,8 +542,7 @@ func NewPartViewer(acct *AccountView, conf *config.AercConfig,
info := msg.MessageInfo() info := msg.MessageInfo()
for _, f := range conf.Filters { for _, f := range conf.Filters {
mime := strings.ToLower(part.MIMEType) + mime := part.FullMIMEType()
"/" + strings.ToLower(part.MIMESubType)
switch f.FilterType { switch f.FilterType {
case config.FILTER_MIMETYPE: case config.FILTER_MIMETYPE:
if fnmatch.Match(f.Filter, mime, 0) { if fnmatch.Match(f.Filter, mime, 0) {
@ -813,8 +807,8 @@ func newNoFilterConfigured(pv *PartViewer) *ui.Grid {
uiConfig := pv.conf.Ui uiConfig := pv.conf.Ui
noFilter := fmt.Sprintf(`No filter configured for this mimetype ('%s/%s') noFilter := fmt.Sprintf(`No filter configured for this mimetype ('%s')
What would you like to do?`, pv.part.MIMEType, pv.part.MIMESubType) What would you like to do?`, pv.part.FullMIMEType())
grid.AddChild(ui.NewText(noFilter, grid.AddChild(ui.NewText(noFilter,
uiConfig.GetStyle(config.STYLE_TITLE))).At(0, 0) uiConfig.GetStyle(config.STYLE_TITLE))).At(0, 0)
for i, action := range actions { for i, action := range actions {