aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md3
-rw-r--r--commands/msgview/open.go8
-rw-r--r--config/aerc.conf14
-rw-r--r--config/config.go15
-rw-r--r--doc/aerc-config.5.scd19
-rw-r--r--doc/aerc.1.scd14
-rw-r--r--lib/open.go40
7 files changed, 104 insertions, 9 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 83e3eb2d..7fb0cb15 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Bracketed paste support.
- Display current directory in `status-line.render-format` with `%p`.
- Change accounts while composing a message with `:switch-account`.
+- Override `:open` handler on a per-MIME-type basis in `aerc.conf`.
+- Specify opener as the first `:open` param instead of always using default
+ handler (i.e. `:open gimp` to open attachment in GIMP).
### Changed
diff --git a/commands/msgview/open.go b/commands/msgview/open.go
index 82c1accb..bb22026a 100644
--- a/commands/msgview/open.go
+++ b/commands/msgview/open.go
@@ -48,10 +48,11 @@ func (Open) Execute(aerc *widgets.Aerc, args []string) error {
mv.MessageView().FetchBodyPart(p.Index, func(reader io.Reader) {
extension := ""
+ mimeType := ""
+
// 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 = fmt.Sprintf("%s/%s", part.MIMEType, part.MIMESubType)
if exts, _ := mime.ExtensionsByType(mimeType); len(exts) > 0 {
extension = exts[0]
}
@@ -71,7 +72,8 @@ func (Open) Execute(aerc *widgets.Aerc, args []string) error {
}
go func() {
- err = lib.XDGOpen(tmpFile.Name())
+ openers := aerc.Config().Openers
+ err = lib.XDGOpenMime(tmpFile.Name(), mimeType, openers, args[1:])
if err != nil {
aerc.PushError("open: " + err.Error())
}
diff --git a/config/aerc.conf b/config/aerc.conf
index 6e5efe7d..0bd96fb5 100644
--- a/config/aerc.conf
+++ b/config/aerc.conf
@@ -308,6 +308,20 @@ text/plain=sed 's/^>\+.*/\x1b[36m&\x1b[0m/'
#text/html=w3m -dump -I UTF-8 -T text/html
#image/*=catimg -w $(tput cols) -
+[openers]
+#
+# Openers allow you to specify the command to use for the :open action on a
+# per-MIME-type basis.
+#
+# {} is expanded as the temporary filename to be opened. If it is not
+# encountered in the command, the temporary filename will be appened to the end
+# of the command.
+#
+# Examples:
+# text/html=surf -dfgms
+# text/plain=gvim {} +125
+# message/rfc822=thunderbird
+
[triggers]
#
# Triggers specify commands to execute when certain events occur.
diff --git a/config/config.go b/config/config.go
index 24a1b507..e31d1a14 100644
--- a/config/config.go
+++ b/config/config.go
@@ -17,6 +17,7 @@ import (
"github.com/gdamore/tcell/v2"
"github.com/go-ini/ini"
+ "github.com/google/shlex"
"github.com/imdario/mergo"
"github.com/kyoh86/xdg"
"github.com/mitchellh/go-homedir"
@@ -257,6 +258,7 @@ type AercConfig struct {
ContextualUis []UIConfigContext
General GeneralConfig
Templates TemplateConfig
+ Openers map[string][]string
}
// Input: TimestampFormat
@@ -484,6 +486,16 @@ func (config *AercConfig) LoadConfig(file *ini.File) error {
config.Filters = append(config.Filters, filter)
}
}
+ if openers, err := file.GetSection("openers"); err == nil {
+ for mimeType, command := range openers.KeysHash() {
+ mimeType = strings.ToLower(mimeType)
+ if args, err := shlex.Split(command); err != nil {
+ return err
+ } else {
+ config.Openers[mimeType] = args
+ }
+ }
+ }
if viewer, err := file.GetSection("viewer"); err == nil {
if err := viewer.MapTo(&config.Viewer); err != nil {
return err
@@ -807,6 +819,8 @@ func LoadConfigFromFile(root *string, accts []string) (*AercConfig, error) {
QuotedReply: "quoted_reply",
Forwards: "forward_as_body",
},
+
+ Openers: make(map[string][]string),
}
// These bindings are not configurable
@@ -835,6 +849,7 @@ func LoadConfigFromFile(root *string, accts []string) (*AercConfig, error) {
logging.Debugf("aerc.conf: [viewer] %#v", config.Viewer)
logging.Debugf("aerc.conf: [compose] %#v", config.Compose)
logging.Debugf("aerc.conf: [filters] %#v", config.Filters)
+ logging.Debugf("aerc.conf: [openers] %#v", config.Openers)
logging.Debugf("aerc.conf: [triggers] %#v", config.Triggers)
logging.Debugf("aerc.conf: [templates] %#v", config.Templates)
diff --git a/doc/aerc-config.5.scd b/doc/aerc-config.5.scd
index d6b56fa0..8b7aa55c 100644
--- a/doc/aerc-config.5.scd
+++ b/doc/aerc-config.5.scd
@@ -508,6 +508,25 @@ that aerc does not have alone.
Note that said email body is converted into UTF-8 before being passed to
filters.
+## OPENERS
+
+Openers allow you to specify the command to use for the *:open* action on a
+per-MIME-type basis. They are configured in the *[openers]* section of
+aerc.conf.
+
+*{}* is expanded as the temporary filename to be opened. If it is not
+encountered in the command, the temporary filename will be appened to the end
+of the command. Environment variables are also expanded. Tilde is not expanded.
+
+Example:
+
+```
+[openers]
+text/html=surf -dfgms
+text/plain=gvim {} +125
+message/rfc822=thunderbird
+```
+
## TRIGGERS
Triggers specify commands to execute when certain events occur.
diff --git a/doc/aerc.1.scd b/doc/aerc.1.scd
index 38862a53..355a08d6 100644
--- a/doc/aerc.1.scd
+++ b/doc/aerc.1.scd
@@ -392,8 +392,18 @@ message list, the message in the message viewer, etc).
at the bottom of the message viewer.
*open* [args...]
- Saves the current message part in a temporary file and opens it
- with the system handler. Any given args are forwarded to the open handler
+ Saves the current message part to a temporary file, then opens it. If no
+ arguments are provided, it will open the current MIME part with the
+ matching command in the *[openers]* section of _aerc.conf_. When no match
+ is found in *[openers]*, it falls back to the default system handler.
+
+ When arguments are provided:
+
+ - The first argument must be the program to open the message part with.
+ Subsequent args are passed to that program.
+ - *{}* will be expanded as the temporary filename to be opened. If it is
+ not encountered in the arguments, the temporary filename will be
+ appened to the end of the command.
*save* [-fpa] <path>
Saves the current message part to the given path.
diff --git a/lib/open.go b/lib/open.go
index a189980a..e091d913 100644
--- a/lib/open.go
+++ b/lib/open.go
@@ -4,16 +4,48 @@ import (
"fmt"
"os/exec"
"runtime"
+ "strings"
"git.sr.ht/~rjarry/aerc/logging"
)
func XDGOpen(uri string) error {
- openBin := "xdg-open"
- if runtime.GOOS == "darwin" {
- openBin = "open"
+ return XDGOpenMime(uri, "", nil, nil)
+}
+
+func XDGOpenMime(
+ uri string, mimeType string,
+ openers map[string][]string, args []string,
+) error {
+ if len(args) == 0 {
+ // no explicit command provided, lookup opener from mime type
+ opener, ok := openers[mimeType]
+ if ok {
+ args = opener
+ } else {
+ // no opener defined in config, fallback to default
+ if runtime.GOOS == "darwin" {
+ args = append(args, "open")
+ } else {
+ args = append(args, "xdg-open")
+ }
+ }
+ }
+
+ i := 0
+ for ; i < len(args); i++ {
+ if strings.Contains(args[i], "{}") {
+ break
+ }
+ }
+ if i < len(args) {
+ // found {} placeholder in args, replace with uri
+ args[i] = strings.Replace(args[i], "{}", uri, 1)
+ } else {
+ // no {} placeholder in args, add uri at the end
+ args = append(args, uri)
}
- args := []string{openBin, uri}
+
logging.Infof("running command: %v", args)
cmd := exec.Command(args[0], args[1:]...)
out, err := cmd.CombinedOutput()