diff --git a/commands/msg/forward.go b/commands/msg/forward.go index 4adfd12..78c2438 100644 --- a/commands/msg/forward.go +++ b/commands/msg/forward.go @@ -198,7 +198,7 @@ func (forward) Execute(aerc *widgets.Aerc, args []string) error { continue } 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) name, ok := params["name"] if !ok { diff --git a/commands/msg/recall.go b/commands/msg/recall.go index c5585b0..866266f 100644 --- a/commands/msg/recall.go +++ b/commands/msg/recall.go @@ -204,7 +204,7 @@ func (Recall) Execute(aerc *widgets.Aerc, args []string) error { continue } msg.FetchBodyPart(p, func(reader io.Reader) { - mime := fmt.Sprintf("%s/%s", bs.MIMEType, bs.MIMESubType) + mime := bs.FullMIMEType() params := lib.SetUtf8Charset(bs.Params) name, ok := params["name"] if !ok { diff --git a/commands/msg/reply.go b/commands/msg/reply.go index 1baef83..f577a96 100644 --- a/commands/msg/reply.go +++ b/commands/msg/reply.go @@ -326,7 +326,7 @@ func addMimeType(msg *models.MessageInfo, part []int, if err != nil { return err } - orig.MIMEType = fmt.Sprintf("%s/%s", bs.MIMEType, bs.MIMESubType) + orig.MIMEType = bs.FullMIMEType() return nil } diff --git a/commands/msgview/open.go b/commands/msgview/open.go index bb22026..13bd4b1 100644 --- a/commands/msgview/open.go +++ b/commands/msgview/open.go @@ -1,7 +1,6 @@ package msgview import ( - "fmt" "io" "mime" "os" @@ -52,7 +51,7 @@ func (Open) Execute(aerc *widgets.Aerc, args []string) error { // try to determine the correct extension based on mimetype 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 { extension = exts[0] } diff --git a/commands/msgview/save.go b/commands/msgview/save.go index 993f427..4820ec0 100644 --- a/commands/msgview/save.go +++ b/commands/msgview/save.go @@ -215,12 +215,7 @@ func isAbsPath(path string) bool { // generateFilename tries to get the filename from the given part. // if that fails it will fallback to a generated one based on the date func generateFilename(part *models.BodyStructure) string { - var filename string - if fn, ok := part.DispositionParams["filename"]; ok { - filename = fn - } else if fn, ok := part.Params["name"]; ok { - filename = fn - } + filename := part.FileName() // 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 // portion of the pathname as the filename for saving it. diff --git a/lib/structure_helpers.go b/lib/structure_helpers.go index 6f25e45..0189dc8 100644 --- a/lib/structure_helpers.go +++ b/lib/structure_helpers.go @@ -9,8 +9,7 @@ import ( func FindPlaintext(bs *models.BodyStructure, path []int) []int { for i, part := range bs.Parts { cur := append(path, i+1) //nolint:gocritic // intentional append to different slice - if strings.ToLower(part.MIMEType) == "text" && - strings.ToLower(part.MIMESubType) == "plain" { + if part.FullMIMEType() == "text/plain" { return cur } 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 { for i, part := range bs.Parts { cur := append(path, i+1) //nolint:gocritic // intentional append to different slice - if strings.ToLower(part.MIMEType) == "text" && - strings.ToLower(part.MIMESubType) == "calendar" { + if part.FullMIMEType() == "text/calendar" { return cur } if strings.ToLower(part.MIMEType) == "multipart" { diff --git a/models/models.go b/models/models.go index 820bc3f..b55024a 100644 --- a/models/models.go +++ b/models/models.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "io" + "strings" "time" "github.com/emersion/go-message/mail" @@ -175,6 +176,21 @@ func (bs *BodyStructure) PartAtIndex(index []int) (*BodyStructure, error) { 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 { Date time.Time Subject string diff --git a/widgets/msgviewer.go b/widgets/msgviewer.go index 19a28f0..70c079a 100644 --- a/widgets/msgviewer.go +++ b/widgets/msgviewer.go @@ -230,8 +230,7 @@ func createSwitcher(acct *AccountView, switcher *PartSwitcher, if switcher.selected == -1 && pv.part.MIMEType != "multipart" { switcher.selected = i } - mime := strings.ToLower(pv.part.MIMEType) + - "/" + strings.ToLower(pv.part.MIMESubType) + mime := pv.part.FullMIMEType() for idx, m := range conf.Viewer.Alternatives { if m != mime { continue @@ -422,13 +421,9 @@ func (ps *PartSwitcher) Draw(ctx *ui.Context) { style = ps.mv.uiConfig.GetStyleSelected(config.STYLE_DEFAULT) } ctx.Fill(0, y+i, ctx.Width(), 1, ' ', style) - name := fmt.Sprintf("%s/%s", - strings.ToLower(part.part.MIMEType), - strings.ToLower(part.part.MIMESubType)) - 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 := part.part.FullMIMEType() + filename := part.part.FileName() + if filename != "" { name += fmt.Sprintf(" (%s)", filename) } 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() for _, f := range conf.Filters { - mime := strings.ToLower(part.MIMEType) + - "/" + strings.ToLower(part.MIMESubType) + mime := part.FullMIMEType() switch f.FilterType { case config.FILTER_MIMETYPE: if fnmatch.Match(f.Filter, mime, 0) { @@ -813,8 +807,8 @@ func newNoFilterConfigured(pv *PartViewer) *ui.Grid { uiConfig := pv.conf.Ui - noFilter := fmt.Sprintf(`No filter configured for this mimetype ('%s/%s') -What would you like to do?`, pv.part.MIMEType, pv.part.MIMESubType) + noFilter := fmt.Sprintf(`No filter configured for this mimetype ('%s') +What would you like to do?`, pv.part.FullMIMEType()) grid.AddChild(ui.NewText(noFilter, uiConfig.GetStyle(config.STYLE_TITLE))).At(0, 0) for i, action := range actions {