mirror of
https://blitiri.com.ar/repos/chasquid
synced 2025-12-17 14:37:02 +00:00
aliases: Support '*' as the destination user
This patch implements support for aliases that contain '*' as the destination user. In that case, we replace it with the original user. For example, `*: *@pond` will redirect `lilly@domain` to `lilly@pond`. This is experimental for now, and marked as such in the documentation. The semantics can be subtle, so we may need to adjust them later.
This commit is contained in:
@@ -13,6 +13,7 @@ markdown_extensions:
|
|||||||
- codehilite:
|
- codehilite:
|
||||||
guess_lang: false
|
guess_lang: false
|
||||||
- attr_list
|
- attr_list
|
||||||
|
- admonition
|
||||||
|
|
||||||
theme: readthedocs
|
theme: readthedocs
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,17 @@ pepe: jose
|
|||||||
*: pepe, rose@backgarden
|
*: pepe, rose@backgarden
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! warning "Experimental"
|
||||||
|
|
||||||
|
If the destination address has `*` as its user, then it will be replaced
|
||||||
|
by the sender user. Note that in this case, the user is copied as-is, no
|
||||||
|
characters or suffixes will be dropped.
|
||||||
|
|
||||||
|
For example, `*: *@pond` will redirect `lilly@domain` to `lilly@pond`.
|
||||||
|
|
||||||
|
This is experimental as of chasquid 1.16.0, and subject to change.
|
||||||
|
|
||||||
|
|
||||||
### Overrides
|
### Overrides
|
||||||
|
|
||||||
If the same left-side address appears more than once, the last one will take
|
If the same left-side address appears more than once, the last one will take
|
||||||
|
|||||||
@@ -286,6 +286,16 @@ func (v *Resolver) resolve(rcount int, addr string, tr *trace.Trace) ([]Recipien
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the user of the destination is "*", then we replace it with the
|
||||||
|
// original user.
|
||||||
|
// This allows for catch-all aliases that forward using the original
|
||||||
|
// user, like "*: *@otherdomain".
|
||||||
|
if envelope.UserOf(r.Addr) == "*" {
|
||||||
|
newAddr := user + "@" + envelope.DomainOf(r.Addr)
|
||||||
|
tr.Debugf("%d| replacing %q with %q", rcount, r.Addr, newAddr)
|
||||||
|
r.Addr = newAddr
|
||||||
|
}
|
||||||
|
|
||||||
ar, err := v.resolve(rcount+1, r.Addr, tr)
|
ar, err := v.resolve(rcount+1, r.Addr, tr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tr.Debugf("%d| resolve(%q) returned error: %v", rcount, r.Addr, err)
|
tr.Debugf("%d| resolve(%q) returned error: %v", rcount, r.Addr, err)
|
||||||
|
|||||||
@@ -62,6 +62,10 @@ func allUsersExist(tr *trace.Trace, user, domain string) (bool, error) {
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func noUsersExist(tr *trace.Trace, user, domain string) (bool, error) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
func usersWithXDontExist(tr *trace.Trace, user, domain string) (bool, error) {
|
func usersWithXDontExist(tr *trace.Trace, user, domain string) (bool, error) {
|
||||||
if strings.HasPrefix(user, "x") {
|
if strings.HasPrefix(user, "x") {
|
||||||
return false, nil
|
return false, nil
|
||||||
@@ -104,6 +108,8 @@ func TestBasic(t *testing.T) {
|
|||||||
|
|
||||||
func TestCatchAll(t *testing.T) {
|
func TestCatchAll(t *testing.T) {
|
||||||
resolver := NewResolver(usersWithXDontExist)
|
resolver := NewResolver(usersWithXDontExist)
|
||||||
|
resolver.DropChars = "."
|
||||||
|
resolver.SuffixSep = "+"
|
||||||
resolver.AddDomain("dom")
|
resolver.AddDomain("dom")
|
||||||
resolver.aliases = map[string][]Recipient{
|
resolver.aliases = map[string][]Recipient{
|
||||||
"a@dom": {{"a@remote", EMAIL}},
|
"a@dom": {{"a@remote", EMAIL}},
|
||||||
@@ -114,6 +120,8 @@ func TestCatchAll(t *testing.T) {
|
|||||||
|
|
||||||
cases := Cases{
|
cases := Cases{
|
||||||
{"a@dom", []Recipient{{"a@remote", EMAIL}}, nil},
|
{"a@dom", []Recipient{{"a@remote", EMAIL}}, nil},
|
||||||
|
{"a+z@dom", []Recipient{{"a@remote", EMAIL}}, nil},
|
||||||
|
{"a.@dom", []Recipient{{"a@remote", EMAIL}}, nil},
|
||||||
{"b@dom", []Recipient{{"cmd", PIPE}}, nil},
|
{"b@dom", []Recipient{{"cmd", PIPE}}, nil},
|
||||||
{"c@dom", []Recipient{{"cmd", PIPE}}, nil},
|
{"c@dom", []Recipient{{"cmd", PIPE}}, nil},
|
||||||
{"x@dom", []Recipient{{"cmd", PIPE}}, nil},
|
{"x@dom", []Recipient{{"cmd", PIPE}}, nil},
|
||||||
@@ -132,6 +140,80 @@ func TestCatchAll(t *testing.T) {
|
|||||||
"x@dom", "x1@dom")
|
"x@dom", "x1@dom")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRightSideAsterisk(t *testing.T) {
|
||||||
|
resolver := NewResolver(noUsersExist)
|
||||||
|
resolver.DropChars = "."
|
||||||
|
resolver.SuffixSep = "+"
|
||||||
|
resolver.AddDomain("dom1")
|
||||||
|
resolver.AddDomain("dom2")
|
||||||
|
resolver.AddDomain("dom3")
|
||||||
|
resolver.AddDomain("dom4")
|
||||||
|
resolver.AddDomain("dom5")
|
||||||
|
resolver.aliases = map[string][]Recipient{
|
||||||
|
"a@dom1": {{"aaa@remote", EMAIL}},
|
||||||
|
|
||||||
|
// Note this goes to dom2 which is local too, and will be resolved
|
||||||
|
// recursively.
|
||||||
|
"*@dom1": {{"*@dom2", EMAIL}},
|
||||||
|
|
||||||
|
"b@dom2": {{"bbb@remote", EMAIL}},
|
||||||
|
"*@dom2": {{"*@remote", EMAIL}},
|
||||||
|
|
||||||
|
// A right hand asterisk on a specific address isn't very useful, but
|
||||||
|
// it is supported.
|
||||||
|
"z@dom1": {{"*@remote", EMAIL}},
|
||||||
|
|
||||||
|
// Asterisk to asterisk creates an infinite loop.
|
||||||
|
"*@dom3": {{"*@dom3", EMAIL}},
|
||||||
|
|
||||||
|
// A right-side asterisk as part of multiple addresses, some of which
|
||||||
|
// are fixed.
|
||||||
|
"*@dom4": {{"*@remote1", EMAIL}, {"*@remote2", EMAIL},
|
||||||
|
{"fixed@remote3", EMAIL}},
|
||||||
|
|
||||||
|
// A chain of a -> b -> * -> *@remote.
|
||||||
|
// This checks which one is used as the "original" user.
|
||||||
|
"a@dom5": {{"b@dom5", EMAIL}},
|
||||||
|
"*@dom5": {{"*@remote", EMAIL}},
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := Cases{
|
||||||
|
{"a@dom1", []Recipient{{"aaa@remote", EMAIL}}, nil},
|
||||||
|
{"b@dom1", []Recipient{{"bbb@remote", EMAIL}}, nil},
|
||||||
|
{"xyz@dom1", []Recipient{{"xyz@remote", EMAIL}}, nil},
|
||||||
|
{"xyz@dom2", []Recipient{{"xyz@remote", EMAIL}}, nil},
|
||||||
|
{"z@dom1", []Recipient{{"z@remote", EMAIL}}, nil},
|
||||||
|
|
||||||
|
// Check that we match after dropping the characters as needed.
|
||||||
|
// This is not specific to the right side asterisk, but serve to
|
||||||
|
// confirm we're not matching against it by accident.
|
||||||
|
{"a+lala@dom1", []Recipient{{"aaa@remote", EMAIL}}, nil},
|
||||||
|
{"a..@dom1", []Recipient{{"aaa@remote", EMAIL}}, nil},
|
||||||
|
|
||||||
|
// Check we don't remove drop characters or suffixes when doing the
|
||||||
|
// rewrite: we expect to pass addresses as they come if they didn't
|
||||||
|
// match previously.
|
||||||
|
{"xyz+abcd@dom1", []Recipient{{"xyz+abcd@remote", EMAIL}}, nil},
|
||||||
|
{"x.y.z@dom1", []Recipient{{"x.y.z@remote", EMAIL}}, nil},
|
||||||
|
|
||||||
|
// This one should fail because it creates an infinite loop.
|
||||||
|
{"x@dom3", nil, ErrRecursionLimitExceeded},
|
||||||
|
|
||||||
|
// Check the multiple addresses case.
|
||||||
|
{"abc@dom4", []Recipient{
|
||||||
|
{"abc@remote1", EMAIL},
|
||||||
|
{"abc@remote2", EMAIL},
|
||||||
|
{"fixed@remote3", EMAIL},
|
||||||
|
}, nil},
|
||||||
|
|
||||||
|
// Check the chain case: a -> b -> * -> remote.
|
||||||
|
{"a@dom5", []Recipient{{"b@remote", EMAIL}}, nil},
|
||||||
|
{"b@dom5", []Recipient{{"b@remote", EMAIL}}, nil},
|
||||||
|
{"c@dom5", []Recipient{{"c@remote", EMAIL}}, nil},
|
||||||
|
}
|
||||||
|
cases.check(t, resolver)
|
||||||
|
}
|
||||||
|
|
||||||
func TestUserLookupErrors(t *testing.T) {
|
func TestUserLookupErrors(t *testing.T) {
|
||||||
resolver := NewResolver(usersWithXErrorYDontExist)
|
resolver := NewResolver(usersWithXErrorYDontExist)
|
||||||
resolver.AddDomain("dom")
|
resolver.AddDomain("dom")
|
||||||
|
|||||||
Reference in New Issue
Block a user