/framework/cfgparser/imports.go
https://gitlab.com/jasonkumpf/maddy · Go · 173 lines · 122 code · 25 blank · 26 comment · 45 complexity · 2f943c347189de1ff3bf015cc0ea03fc MD5 · raw file
- /*
- Maddy Mail Server - Composable all-in-one email server.
- Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- package parser
- import (
- "os"
- "path/filepath"
- "regexp"
- "strings"
- )
- func (ctx *parseContext) expandImports(node Node, expansionDepth int) (Node, error) {
- // Leave nil value as is because it is used as non-existent block indicator
- // (vs empty slice - empty block).
- if node.Children == nil {
- return node, nil
- }
- newChildrens := make([]Node, 0, len(node.Children))
- containsImports := false
- for _, child := range node.Children {
- child, err := ctx.expandImports(child, expansionDepth+1)
- if err != nil {
- return node, err
- }
- if child.Name == "import" {
- // We check it here instead of function start so we can
- // use line information from import directive that is likely
- // caused this error.
- if expansionDepth > 255 {
- return node, NodeErr(child, "hit import expansion limit")
- }
- containsImports = true
- if len(child.Args) != 1 {
- return node, ctx.Err("import directive requires exactly 1 argument")
- }
- subtree, err := ctx.resolveImport(child, child.Args[0], expansionDepth)
- if err != nil {
- return node, err
- }
- newChildrens = append(newChildrens, subtree...)
- } else {
- newChildrens = append(newChildrens, child)
- }
- }
- node.Children = newChildrens
- // We need to do another pass to expand any imports added by snippets we
- // just expanded.
- if containsImports {
- return ctx.expandImports(node, expansionDepth+1)
- }
- return node, nil
- }
- func (ctx *parseContext) resolveImport(node Node, name string, expansionDepth int) ([]Node, error) {
- if subtree, ok := ctx.snippets[name]; ok {
- return subtree, nil
- }
- file := filepath.Join(filepath.Dir(ctx.fileLocation), name)
- src, err := os.Open(file)
- if err != nil {
- if os.IsNotExist(err) {
- src, err = os.Open(file + ".conf")
- if err != nil {
- if os.IsNotExist(err) {
- return nil, NodeErr(node, "unknown import: "+name)
- }
- return nil, err
- }
- } else {
- return nil, err
- }
- }
- nodes, snips, macros, err := readTree(src, file, expansionDepth+1)
- if err != nil {
- return nodes, err
- }
- for k, v := range snips {
- ctx.snippets[k] = v
- }
- for k, v := range macros {
- ctx.macros[k] = v
- }
- return nodes, nil
- }
- func (ctx *parseContext) expandMacros(node *Node) error {
- if strings.HasPrefix(node.Name, "$(") && strings.HasSuffix(node.Name, ")") {
- return ctx.Err("can't use macro argument as directive name")
- }
- newArgs := make([]string, 0, len(node.Args))
- for _, arg := range node.Args {
- if !strings.HasPrefix(arg, "$(") || !strings.HasSuffix(arg, ")") {
- if strings.Contains(arg, "$(") && strings.Contains(arg, ")") {
- var err error
- arg, err = ctx.expandSingleValueMacro(arg)
- if err != nil {
- return err
- }
- }
- newArgs = append(newArgs, arg)
- continue
- }
- macroName := arg[2 : len(arg)-1]
- replacement, ok := ctx.macros[macroName]
- if !ok {
- // Undefined macros are expanded to zero arguments.
- continue
- }
- newArgs = append(newArgs, replacement...)
- }
- node.Args = newArgs
- if node.Children != nil {
- for i := range node.Children {
- if err := ctx.expandMacros(&node.Children[i]); err != nil {
- return err
- }
- }
- }
- return nil
- }
- var macroRe = regexp.MustCompile(`\$\(([^\$]+)\)`)
- func (ctx *parseContext) expandSingleValueMacro(arg string) (string, error) {
- matches := macroRe.FindAllStringSubmatch(arg, -1)
- for _, match := range matches {
- macroName := match[1]
- if len(ctx.macros[macroName]) > 1 {
- return "", ctx.Err("can't expand macro with multiple arguments inside a string")
- }
- var value string
- if ctx.macros[macroName] != nil {
- // Macros have at least one argument.
- value = ctx.macros[macroName][0]
- }
- arg = strings.Replace(arg, "$("+macroName+")", value, -1)
- }
- return arg, nil
- }