aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Cox <me@jasoncarloscox.com>2023-10-12 10:38:39 -0400
committerRobin Jarry <robin@jarry.cc>2023-10-22 15:12:45 +0200
commit863e124e8437ac2f5a92fe3f291b7e7e022787fc (patch)
tree54aca238e0c622b6a34e4095830c45c264c0fabf
parent8fdabbc4bfcaf8f0214a66777336a356cc2a0616 (diff)
downloadaerc-863e124e8437ac2f5a92fe3f291b7e7e022787fc.tar.gz
aerc-863e124e8437ac2f5a92fe3f291b7e7e022787fc.zip
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.md1
-rw-r--r--commands/msg/invite.go21
-rw-r--r--commands/msg/reply.go52
-rw-r--r--doc/aerc-accounts.5.scd7
-rw-r--r--lib/state/templates.go7
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