1package serve23import (4 "context"5 "net/http"6 "net/url"7 "os"8 "os/signal"9 "strings"10 "sync"11 "syscall"12 "time"1314 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)2223func init() {24 cmd := &cobra.Command{25 Use: "serve",26 Short: "start lmtp service",27 Run: execute,28 }2930 rcmd.Cmd.AddCommand(cmd)31}3233func execute(cmd *cobra.Command, args []string) {34 ctx, cancel := signal.NotifyContext(cmd.Context(), syscall.SIGTERM, os.Interrupt)35 defer cancel()3637 cfg := config.FromContext(ctx)38 l := cfg.NewLogger()3940 st, ok := storage.FromContext(ctx)41 if !ok {42 l.Error("unset storage")43 return44 }4546 // init lmtp server47 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 }5556 be := service.NewLMTPBackend(ctx, st, &mta, l)57 be.ConnTimeout = cfg.LMTP.ConnTimeout5859 lmtpuri, err := url.Parse(cfg.LMTP.Listen)60 if err != nil {61 l.Error(err.Error())62 return63 }6465 s := smtp.NewServer(be)66 s.Network = lmtpuri.Scheme67 s.Addr = strings.Join([]string{lmtpuri.Host, lmtpuri.Path}, "")68 s.LMTP = true69 s.Domain = "localhost"70 s.WriteTimeout = 10 * time.Second71 s.ReadTimeout = 10 * time.Second72 s.MaxMessageBytes = 1024 * 102473 s.MaxRecipients = 5074 s.AllowInsecureAuth = true7576 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 }()8384 // init http server85 h := http.Server{86 Handler: service.NewHTTPMux(cfg, st, l),87 Addr: cfg.Http.Listen,88 }8990 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 }()9697 <-ctx.Done()9899 l.Info("stopping service")100 ctx, cancel = context.WithTimeout(context.TODO(), time.Second*30)101 defer cancel()102103 var exit_wg sync.WaitGroup104 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")110111 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}