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	"fmt"
 24	"regexp"
 25	"strings"
 26
 27	"github.com/foxcpp/maddy/framework/config"
 28	"github.com/foxcpp/maddy/framework/module"
 29)
 30
 31type Regexp struct {
 32	modName    string
 33	instName   string
 34	inlineArgs []string
 35
 36	re           *regexp.Regexp
 37	replacements []string
 38
 39	expandPlaceholders bool
 40}
 41
 42func NewRegexp(modName, instName string, _, inlineArgs []string) (module.Module, error) {
 43	return &Regexp{
 44		modName:    modName,
 45		instName:   instName,
 46		inlineArgs: inlineArgs,
 47	}, nil
 48}
 49
 50func (r *Regexp) Init(cfg *config.Map) error {
 51	var (
 52		fullMatch       bool
 53		caseInsensitive bool
 54	)
 55	cfg.Bool("full_match", false, true, &fullMatch)
 56	cfg.Bool("case_insensitive", false, true, &caseInsensitive)
 57	cfg.Bool("expand_replaceholders", false, true, &r.expandPlaceholders)
 58	if _, err := cfg.Process(); err != nil {
 59		return err
 60	}
 61
 62	regex := r.inlineArgs[0]
 63	if len(r.inlineArgs) > 1 {
 64		r.replacements = r.inlineArgs[1:]
 65	}
 66
 67	if fullMatch {
 68		if !strings.HasPrefix(regex, "^") {
 69			regex = "^" + regex
 70		}
 71		if !strings.HasSuffix(regex, "$") {
 72			regex = regex + "$"
 73		}
 74	}
 75
 76	if caseInsensitive {
 77		regex = "(?i)" + regex
 78	}
 79
 80	var err error
 81	r.re, err = regexp.Compile(regex)
 82	if err != nil {
 83		return fmt.Errorf("%s: %v", r.modName, err)
 84	}
 85	return nil
 86}
 87
 88func (r *Regexp) Name() string {
 89	return r.modName
 90}
 91
 92func (r *Regexp) InstanceName() string {
 93	return r.modName
 94}
 95
 96func (r *Regexp) LookupMulti(_ context.Context, key string) ([]string, error) {
 97	matches := r.re.FindStringSubmatchIndex(key)
 98	if matches == nil {
 99		return []string{}, nil
100	}
101
102	result := []string{}
103	for _, replacement := range r.replacements {
104		if !r.expandPlaceholders {
105			result = append(result, replacement)
106		} else {
107			result = append(result, string(r.re.ExpandString([]byte{}, replacement, key, matches)))
108		}
109	}
110	return result, nil
111}
112
113func (r *Regexp) Lookup(ctx context.Context, key string) (string, bool, error) {
114	newVal, err := r.LookupMulti(ctx, key)
115	if err != nil {
116		return "", false, err
117	}
118	if len(newVal) == 0 {
119		return "", false, nil
120	}
121
122	return newVal[0], true, nil
123}
124
125func init() {
126	module.Register("table.regexp", NewRegexp)
127}