1/*2Maddy Mail Server - Composable all-in-one email server.3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors45This program is free software: you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation, either version 3 of the License, or8(at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program. If not, see <https://www.gnu.org/licenses/>.17*/1819package modify2021import (22 "context"2324 "github.com/emersion/go-message/textproto"25 "github.com/foxcpp/maddy/framework/buffer"26 "github.com/foxcpp/maddy/framework/config"27 modconfig "github.com/foxcpp/maddy/framework/config/module"28 "github.com/foxcpp/maddy/framework/module"29)3031type (32 // Group wraps multiple modifiers and runs them serially.33 //34 // It is also registered as a module under 'modifiers' name and acts as a35 // module group.36 Group struct {37 instName string38 Modifiers []module.Modifier39 }4041 groupState struct {42 states []module.ModifierState43 }44)4546func (g *Group) Init(cfg *config.Map) error {47 for _, node := range cfg.Block.Children {48 mod, err := modconfig.MsgModifier(cfg.Globals, append([]string{node.Name}, node.Args...), node)49 if err != nil {50 return err51 }5253 g.Modifiers = append(g.Modifiers, mod)54 }5556 return nil57}5859func (g *Group) Name() string {60 return "modifiers"61}6263func (g *Group) InstanceName() string {64 return g.instName65}6667func (g Group) ModStateForMsg(ctx context.Context, msgMeta *module.MsgMetadata) (module.ModifierState, error) {68 gs := groupState{}69 for _, modifier := range g.Modifiers {70 state, err := modifier.ModStateForMsg(ctx, msgMeta)71 if err != nil {72 // Free state objects we initialized already.73 for _, state := range gs.states {74 state.Close()75 }76 return nil, err77 }78 gs.states = append(gs.states, state)79 }80 return gs, nil81}8283func (gs groupState) RewriteSender(ctx context.Context, mailFrom string) (string, error) {84 var err error85 for _, state := range gs.states {86 mailFrom, err = state.RewriteSender(ctx, mailFrom)87 if err != nil {88 return "", err89 }90 }91 return mailFrom, nil92}9394func (gs groupState) RewriteRcpt(ctx context.Context, rcptTo string) ([]string, error) {95 var err error96 var result = []string{rcptTo}97 for _, state := range gs.states {98 var intermediateResult = []string{}99 for _, partResult := range result {100 var partResult_multi []string101 partResult_multi, err = state.RewriteRcpt(ctx, partResult)102 if err != nil {103 return []string{""}, err104 }105 intermediateResult = append(intermediateResult, partResult_multi...)106 }107 result = intermediateResult108 }109 return result, nil110}111112func (gs groupState) RewriteBody(ctx context.Context, h *textproto.Header, body buffer.Buffer) error {113 for _, state := range gs.states {114 if err := state.RewriteBody(ctx, h, body); err != nil {115 return err116 }117 }118 return nil119}120121func (gs groupState) Close() error {122 // We still try close all state objects to minimize123 // resource leaks when Close fails for one object..124125 var lastErr error126 for _, state := range gs.states {127 if err := state.Close(); err != nil {128 lastErr = err129 }130 }131 return lastErr132}133134func init() {135 module.Register("modifiers", func(_, instName string, _, _ []string) (module.Module, error) {136 return &Group{137 instName: instName,138 }, nil139 })140}