mlisting

Mailing list service

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

  1package serve
  2
  3import (
  4	"context"
  5	"net/http"
  6	"net/url"
  7	"os"
  8	"os/signal"
  9	"strings"
 10	"sync"
 11	"syscall"
 12	"time"
 13
 14	rcmd "git.lin.moe/go/mlisting/cmd"
 15	"git.lin.moe/go/mlisting/config"
 16	"git.lin.moe/go/mlisting/service"
 17	"git.lin.moe/go/mlisting/storage"
 18	"github.com/emersion/go-sasl"
 19	"github.com/emersion/go-smtp"
 20	"github.com/spf13/cobra"
 21)
 22
 23func init() {
 24	cmd := &cobra.Command{
 25		Use:   "serve",
 26		Short: "start lmtp service",
 27		Run:   execute,
 28	}
 29
 30	rcmd.Cmd.AddCommand(cmd)
 31}
 32
 33func execute(cmd *cobra.Command, args []string) {
 34	ctx, cancel := signal.NotifyContext(cmd.Context(), syscall.SIGTERM, os.Interrupt)
 35	defer cancel()
 36
 37	cfg := config.FromContext(ctx)
 38	l := cfg.NewLogger()
 39
 40	st, ok := storage.FromContext(ctx)
 41	if !ok {
 42		l.Error("unset storage")
 43		return
 44	}
 45
 46	// init lmtp server
 47	mta := service.SMTPClient{
 48		Address:    cfg.SMTP.Address,
 49		ConnType:   cfg.SMTP.ConnType,
 50		AuthClient: nil,
 51	}
 52	if cfg.SMTP.AuthUser != "" || cfg.SMTP.AuthPassword != "" {
 53		mta.AuthClient = sasl.NewPlainClient("", cfg.SMTP.AuthUser, cfg.SMTP.AuthPassword)
 54	}
 55
 56	be := service.NewLMTPBackend(ctx, st, &mta, l)
 57	be.ConnTimeout = cfg.LMTP.ConnTimeout
 58
 59	lmtpuri, err := url.Parse(cfg.LMTP.Listen)
 60	if err != nil {
 61		l.Error(err.Error())
 62		return
 63	}
 64
 65	s := smtp.NewServer(be)
 66	s.Network = lmtpuri.Scheme
 67	s.Addr = strings.Join([]string{lmtpuri.Host, lmtpuri.Path}, "")
 68	s.LMTP = true
 69	s.Domain = "localhost"
 70	s.WriteTimeout = 10 * time.Second
 71	s.ReadTimeout = 10 * time.Second
 72	s.MaxMessageBytes = 1024 * 1024
 73	s.MaxRecipients = 50
 74	s.AllowInsecureAuth = true
 75
 76	go func() {
 77		l.Info("lmtp service start", "listen", lmtpuri)
 78		if err := s.ListenAndServe(); err != nil {
 79			l.Error(err.Error())
 80			cancel()
 81		}
 82	}()
 83
 84	// init http server
 85	h := http.Server{
 86		Handler: service.NewHTTPMux(cfg, st, l),
 87		Addr:    cfg.Http.Listen,
 88	}
 89
 90	go func() {
 91		l.Info("http service start", "listen", cfg.Http.Listen)
 92		if err := h.ListenAndServe(); err != nil {
 93			l.Error(err.Error())
 94		}
 95	}()
 96
 97	<-ctx.Done()
 98
 99	l.Info("stopping service")
100	ctx, cancel = context.WithTimeout(context.TODO(), time.Second*30)
101	defer cancel()
102
103	var exit_wg sync.WaitGroup
104	exit_wg.Add(1)
105	go func() {
106		if err := h.Shutdown(ctx); err != nil {
107			l.Error(err.Error())
108		}
109		l.Info("http service stopped")
110
111		exit_wg.Done()
112	}()
113	exit_wg.Add(1)
114	go func() {
115		if err := s.Shutdown(ctx); err != nil {
116			l.Error(err.Error())
117		}
118		l.Info("lmtp service stopped")
119		exit_wg.Done()
120	}()
121	exit_wg.Wait()
122	l.Info("service stopped")
123}