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 ctl
 20
 21import (
 22	"errors"
 23	"fmt"
 24	"io"
 25	"os"
 26
 27	"github.com/foxcpp/maddy"
 28	parser "github.com/foxcpp/maddy/framework/cfgparser"
 29	"github.com/foxcpp/maddy/framework/config"
 30	"github.com/foxcpp/maddy/framework/hooks"
 31	"github.com/foxcpp/maddy/framework/module"
 32	"github.com/foxcpp/maddy/internal/updatepipe"
 33	"github.com/urfave/cli/v2"
 34)
 35
 36func closeIfNeeded(i interface{}) {
 37	if c, ok := i.(io.Closer); ok {
 38		c.Close()
 39	}
 40}
 41
 42func getCfgBlockModule(ctx *cli.Context) (map[string]interface{}, *maddy.ModInfo, error) {
 43	cfgPath := ctx.String("config")
 44	if cfgPath == "" {
 45		return nil, nil, cli.Exit("Error: config is required", 2)
 46	}
 47	cfgFile, err := os.Open(cfgPath)
 48	if err != nil {
 49		return nil, nil, cli.Exit(fmt.Sprintf("Error: failed to open config: %v", err), 2)
 50	}
 51	defer cfgFile.Close()
 52	cfgNodes, err := parser.Read(cfgFile, cfgFile.Name())
 53	if err != nil {
 54		return nil, nil, cli.Exit(fmt.Sprintf("Error: failed to parse config: %v", err), 2)
 55	}
 56
 57	globals, cfgNodes, err := maddy.ReadGlobals(cfgNodes)
 58	if err != nil {
 59		return nil, nil, err
 60	}
 61
 62	if err := maddy.InitDirs(); err != nil {
 63		return nil, nil, err
 64	}
 65
 66	module.NoRun = true
 67	_, mods, err := maddy.RegisterModules(globals, cfgNodes)
 68	if err != nil {
 69		return nil, nil, err
 70	}
 71	defer hooks.RunHooks(hooks.EventShutdown)
 72
 73	cfgBlock := ctx.String("cfg-block")
 74	if cfgBlock == "" {
 75		return nil, nil, cli.Exit("Error: cfg-block is required", 2)
 76	}
 77	var mod maddy.ModInfo
 78	for _, m := range mods {
 79		if m.Instance.InstanceName() == cfgBlock {
 80			mod = m
 81			break
 82		}
 83	}
 84	if mod.Instance == nil {
 85		return nil, nil, cli.Exit(fmt.Sprintf("Error: unknown configuration block: %s", cfgBlock), 2)
 86	}
 87
 88	return globals, &mod, nil
 89}
 90
 91func openStorage(ctx *cli.Context) (module.Storage, error) {
 92	globals, mod, err := getCfgBlockModule(ctx)
 93	if err != nil {
 94		return nil, err
 95	}
 96
 97	storage, ok := mod.Instance.(module.Storage)
 98	if !ok {
 99		return nil, cli.Exit(fmt.Sprintf("Error: configuration block %s is not an IMAP storage", ctx.String("cfg-block")), 2)
100	}
101
102	if err := mod.Instance.Init(config.NewMap(globals, mod.Cfg)); err != nil {
103		return nil, fmt.Errorf("Error: module initialization failed: %w", err)
104	}
105
106	if updStore, ok := mod.Instance.(updatepipe.Backend); ok {
107		if err := updStore.EnableUpdatePipe(updatepipe.ModePush); err != nil && !errors.Is(err, os.ErrNotExist) {
108			fmt.Fprintf(os.Stderr, "Failed to initialize update pipe, do not remove messages from mailboxes open by clients: %v\n", err)
109		}
110	} else {
111		fmt.Fprintf(os.Stderr, "No update pipe support, do not remove messages from mailboxes open by clients\n")
112	}
113
114	return storage, nil
115}
116
117func openUserDB(ctx *cli.Context) (module.PlainUserDB, error) {
118	globals, mod, err := getCfgBlockModule(ctx)
119	if err != nil {
120		return nil, err
121	}
122
123	userDB, ok := mod.Instance.(module.PlainUserDB)
124	if !ok {
125		return nil, cli.Exit(fmt.Sprintf("Error: configuration block %s is not a local credentials store", ctx.String("cfg-block")), 2)
126	}
127
128	if err := mod.Instance.Init(config.NewMap(globals, mod.Cfg)); err != nil {
129		return nil, fmt.Errorf("Error: module initialization failed: %w", err)
130	}
131
132	return userDB, nil
133}