maddy

Fork https://github.com/foxcpp/maddy

git clone git://git.lin.moe/go/maddy.git

  1/*
  2Maddy Mail Server - Composable all-in-one email server.
  3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors
  4
  5This program is free software: you can redistribute it and/or modify
  6it under the terms of the GNU General Public License as published by
  7the Free Software Foundation, either version 3 of the License, or
  8(at your option) any later version.
  9
 10This program is distributed in the hope that it will be useful,
 11but WITHOUT ANY WARRANTY; without even the implied warranty of
 12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13GNU General Public License for more details.
 14
 15You should have received a copy of the GNU General Public License
 16along with this program.  If not, see <https://www.gnu.org/licenses/>.
 17*/
 18
 19package modify
 20
 21import (
 22	"context"
 23
 24	"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)
 30
 31type (
 32	// Group wraps multiple modifiers and runs them serially.
 33	//
 34	// It is also registered as a module under 'modifiers' name and acts as a
 35	// module group.
 36	Group struct {
 37		instName  string
 38		Modifiers []module.Modifier
 39	}
 40
 41	groupState struct {
 42		states []module.ModifierState
 43	}
 44)
 45
 46func (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 err
 51		}
 52
 53		g.Modifiers = append(g.Modifiers, mod)
 54	}
 55
 56	return nil
 57}
 58
 59func (g *Group) Name() string {
 60	return "modifiers"
 61}
 62
 63func (g *Group) InstanceName() string {
 64	return g.instName
 65}
 66
 67func (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, err
 77		}
 78		gs.states = append(gs.states, state)
 79	}
 80	return gs, nil
 81}
 82
 83func (gs groupState) RewriteSender(ctx context.Context, mailFrom string) (string, error) {
 84	var err error
 85	for _, state := range gs.states {
 86		mailFrom, err = state.RewriteSender(ctx, mailFrom)
 87		if err != nil {
 88			return "", err
 89		}
 90	}
 91	return mailFrom, nil
 92}
 93
 94func (gs groupState) RewriteRcpt(ctx context.Context, rcptTo string) ([]string, error) {
 95	var err error
 96	var result = []string{rcptTo}
 97	for _, state := range gs.states {
 98		var intermediateResult = []string{}
 99		for _, partResult := range result {
100			var partResult_multi []string
101			partResult_multi, err = state.RewriteRcpt(ctx, partResult)
102			if err != nil {
103				return []string{""}, err
104			}
105			intermediateResult = append(intermediateResult, partResult_multi...)
106		}
107		result = intermediateResult
108	}
109	return result, nil
110}
111
112func (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 err
116		}
117	}
118	return nil
119}
120
121func (gs groupState) Close() error {
122	// We still try close all state objects to minimize
123	// resource leaks when Close fails for one object..
124
125	var lastErr error
126	for _, state := range gs.states {
127		if err := state.Close(); err != nil {
128			lastErr = err
129		}
130	}
131	return lastErr
132}
133
134func init() {
135	module.Register("modifiers", func(_, instName string, _, _ []string) (module.Module, error) {
136		return &Group{
137			instName: instName,
138		}, nil
139	})
140}