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 table
 20
 21import (
 22	"context"
 23
 24	"github.com/foxcpp/maddy/framework/config"
 25	modconfig "github.com/foxcpp/maddy/framework/config/module"
 26	"github.com/foxcpp/maddy/framework/module"
 27)
 28
 29type Chain struct {
 30	modName  string
 31	instName string
 32
 33	chain    []module.Table
 34	optional []bool
 35}
 36
 37func NewChain(modName, instName string, _, _ []string) (module.Module, error) {
 38	return &Chain{
 39		modName:  modName,
 40		instName: instName,
 41	}, nil
 42}
 43
 44func (s *Chain) Init(cfg *config.Map) error {
 45	cfg.Callback("step", func(m *config.Map, node config.Node) error {
 46		var tbl module.Table
 47		err := modconfig.ModuleFromNode("table", node.Args, node, m.Globals, &tbl)
 48		if err != nil {
 49			return err
 50		}
 51
 52		s.chain = append(s.chain, tbl)
 53		s.optional = append(s.optional, false)
 54		return nil
 55	})
 56	cfg.Callback("optional_step", func(m *config.Map, node config.Node) error {
 57		var tbl module.Table
 58		err := modconfig.ModuleFromNode("table", node.Args, node, m.Globals, &tbl)
 59		if err != nil {
 60			return err
 61		}
 62
 63		s.chain = append(s.chain, tbl)
 64		s.optional = append(s.optional, true)
 65		return nil
 66	})
 67
 68	_, err := cfg.Process()
 69	return err
 70}
 71
 72func (s *Chain) Name() string {
 73	return s.modName
 74}
 75
 76func (s *Chain) InstanceName() string {
 77	return s.instName
 78}
 79
 80func (s *Chain) Lookup(ctx context.Context, key string) (string, bool, error) {
 81	newVal, err := s.LookupMulti(ctx, key)
 82	if err != nil {
 83		return "", false, err
 84	}
 85	if len(newVal) == 0 {
 86		return "", false, nil
 87	}
 88
 89	return newVal[0], true, nil
 90}
 91
 92func (s *Chain) LookupMulti(ctx context.Context, key string) ([]string, error) {
 93	result := []string{key}
 94STEP:
 95	for i, step := range s.chain {
 96		newResult := []string{}
 97		for _, key = range result {
 98			if step_multi, ok := step.(module.MultiTable); ok {
 99				val, err := step_multi.LookupMulti(ctx, key)
100				if err != nil {
101					return []string{}, err
102				}
103				if len(val) == 0 {
104					if s.optional[i] {
105						continue STEP
106					}
107					return []string{}, nil
108				}
109				newResult = append(newResult, val...)
110			} else {
111				val, ok, err := step.Lookup(ctx, key)
112				if err != nil {
113					return []string{}, err
114				}
115				if !ok {
116					if s.optional[i] {
117						continue STEP
118					}
119					return []string{}, nil
120				}
121				newResult = append(newResult, val)
122			}
123		}
124		result = newResult
125	}
126	return result, nil
127}
128
129func init() {
130	module.Register("table.chain", NewChain)
131}