package services import ( "context" "io" "log" "ur.gs/crockery/internal/imap" "ur.gs/crockery/internal/smtp" "ur.gs/crockery/internal/store" ) type Interface interface { Run() // Run the services } func New(ctx context.Context, datastore store.Interface) (Interface, error) { ctx, cancelFunc := context.WithCancel(ctx) return &concrete{ ctx: ctx, cancel: cancelFunc, store: datastore, }, nil } type genServer interface { Run() io.Closer } type concrete struct { ctx context.Context cancel context.CancelFunc store store.Interface // This is only mutated in the Run() method servers []genServer } // For now, bind unconditionally to these ports, on IPv4 + IPv6: // 25 - incoming SMTP (STARTTLS) // 587 - outgoing SMTP (STARTTLS) // 143 - IMAP (STARTTLS) // 993 - IMAP (TLS) // // Each listener gets incoming connections serviced against the passed-in store. func (c *concrete) Run() { c.servers = []genServer{ smtp.NewServer(c.cancel, c.store, false), smtp.NewServer(c.cancel, c.store, true), imap.NewServer(c.cancel, c.store, false), imap.NewServer(c.cancel, c.store, true), } log.Print("Running all services") for _, srv := range c.servers { go srv.Run() } <-c.ctx.Done() log.Print("Closing all services") for _, srv := range c.servers { srv.Close() } }