diff options
author | Jason Cox <me@jasoncarloscox.com> | 2023-10-12 10:38:39 -0400 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2023-10-22 15:12:45 +0200 |
commit | 863e124e8437ac2f5a92fe3f291b7e7e022787fc (patch) | |
tree | 54aca238e0c622b6a34e4095830c45c264c0fabf | |
parent | 8fdabbc4bfcaf8f0214a66777336a356cc2a0616 (diff) | |
download | aerc-863e124e8437ac2f5a92fe3f291b7e7e022787fc.tar.gz |
accounts: allow fnmatch-style wildcards in aliases
Wildcard aliases make it possible to always reply from the same address
to which a message was addressed, which is useful for catch-all email
domains. Support fnmatch-style wildcards in only the address portion of
an alias.
When replying to a message that matches a wildcard alias, substitute the
matching email address for the wildcard address, but keep the name
specified with the wildcard address. For example, when the alias
"Someone Awesome" <*@someone.com> is present, the reply to an email
addressed to "Someone" <hi@someone.com> would be from
"Someone Awesome" <hi@someone.com>.
Signed-off-by: Jason Cox <me@jasoncarloscox.com>
Acked-by: Robin Jarry <robin@jarry.cc>
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | commands/msg/invite.go | 21 | ||||
-rw-r--r-- | commands/msg/reply.go | 52 | ||||
-rw-r--r-- | doc/aerc-accounts.5.scd | 7 | ||||
-rw-r--r-- | lib/state/templates.go | 7 |
5 files changed, 48 insertions, 40 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 707409ca..54229546 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - New `flagged` criteria for `:sort` - New `:send-keys` command to control embedded terminals. +- Account aliases now support fnmatch-style wildcards ### Fixed diff --git a/commands/msg/invite.go b/commands/msg/invite.go index 76864f9a..60107480 100644 --- a/commands/msg/invite.go +++ b/commands/msg/invite.go @@ -78,26 +78,7 @@ func (invite) Execute(args []string) error { return fmt.Errorf("no participation status defined") } - conf := acct.AccountConfig() - from := conf.From - - // figure out the sending from address if we have aliases - if len(conf.Aliases) != 0 { - rec := newAddrSet() - rec.AddList(msg.Envelope.To) - rec.AddList(msg.Envelope.Cc) - // test the from first, it has priority over any present alias - if rec.Contains(from) { - // do nothing - } else { - for _, a := range conf.Aliases { - if rec.Contains(a) { - from = a - break - } - } - } - } + from := chooseFromAddr(acct.AccountConfig(), msg) var to []*mail.Address diff --git a/commands/msg/reply.go b/commands/msg/reply.go index b9ee050a..fff90fff 100644 --- a/commands/msg/reply.go +++ b/commands/msg/reply.go @@ -20,6 +20,7 @@ import ( "git.sr.ht/~rjarry/aerc/lib/parse" "git.sr.ht/~rjarry/aerc/log" "git.sr.ht/~rjarry/aerc/models" + "github.com/danwakefield/fnmatch" "github.com/emersion/go-message/mail" ) @@ -76,7 +77,6 @@ func (reply) Execute(args []string) error { return errors.New("No account selected") } conf := acct.AccountConfig() - from := conf.From store := widget.Store() if store == nil { @@ -87,23 +87,7 @@ func (reply) Execute(args []string) error { return err } - // figure out the sending from address if we have aliases - if len(conf.Aliases) != 0 { - rec := newAddrSet() - rec.AddList(msg.Envelope.To) - rec.AddList(msg.Envelope.Cc) - // test the from first, it has priority over any present alias - if rec.Contains(from) { - // do nothing - } else { - for _, a := range conf.Aliases { - if rec.Contains(a) { - from = a - break - } - } - } - } + from := chooseFromAddr(conf, msg) var ( to []*mail.Address @@ -279,6 +263,28 @@ func (reply) Execute(args []string) error { } } +func chooseFromAddr(conf *config.AccountConfig, msg *models.MessageInfo) *mail.Address { + if len(conf.Aliases) == 0 { + return conf.From + } + + rec := newAddrSet() + rec.AddList(msg.Envelope.To) + rec.AddList(msg.Envelope.Cc) + // test the from first, it has priority over any present alias + if rec.Contains(conf.From) { + // do nothing + } else { + for _, a := range conf.Aliases { + if match := rec.FindMatch(a); match != "" { + return &mail.Address{Name: a.Name, Address: match} + } + } + } + + return conf.From +} + type addrSet map[string]struct{} func newAddrSet() addrSet { @@ -301,6 +307,16 @@ func (s addrSet) Contains(a *mail.Address) bool { return ok } +func (s addrSet) FindMatch(a *mail.Address) string { + for addr := range s { + if fnmatch.Match(a.Address, addr, 0) { + return addr + } + } + + return "" +} + // setReferencesHeader adds the references header to target based on parent // according to RFC2822 func setReferencesHeader(target, parent *mail.Header) error { diff --git a/doc/aerc-accounts.5.scd b/doc/aerc-accounts.5.scd index 3b6ad030..9d95af32 100644 --- a/doc/aerc-accounts.5.scd +++ b/doc/aerc-accounts.5.scd @@ -126,6 +126,13 @@ Note that many of these configuration options are written for you, such as use *aerc-sendmail*(5) in combination with *msmtp*(1) and *--read-envelope-from*. + An alias can also use fnmatch-style wildcards in the address portion. These + wildcards can be useful for catch-all addresses. For example, the alias + _"Your Name" <\*@you.com>_ would ensure that when replying to emails addressed + to _hi@you.com_ and _contact@you.com_, the From: field is set to + _hi@you.com_ and _contact@you.com_, respectively. The name from the alias, + not from the matching address, is used. + *headers* = _<header1,header2,header3...>_ Specifies the comma separated list of headers to fetch with the message. diff --git a/lib/state/templates.go b/lib/state/templates.go index 88c5eeae..92a25b72 100644 --- a/lib/state/templates.go +++ b/lib/state/templates.go @@ -8,6 +8,7 @@ import ( "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib/parse" "git.sr.ht/~rjarry/aerc/models" + "github.com/danwakefield/fnmatch" sortthread "github.com/emersion/go-imap-sortthread" "github.com/emersion/go-message/mail" ) @@ -218,8 +219,10 @@ func (d *templateData) Peer() []*mail.Address { to, _ = d.headers.AddressList("to") } for _, addr := range from { - if d.myAddresses[addr.Address] { - return to + for myAddr := range d.myAddresses { + if fnmatch.Match(myAddr, addr.Address, 0) { + return to + } } } return from |